Commit d1638d66 authored by Rafaël Carré's avatar Rafaël Carré

include UAC plugin source code for version 0.0.11.d

modifications by me:
- lower case filenames and includes
- Makefile usable with mingw-w64
- fix compilation with mingw-w64
parent 7262d1c9
History:
--------
v0.0.11d - 20090705 (AndersK)
*Fixed UAC_RealWorldFullyLoadedDualModeExample.nsi so installing as admin will allow both modes
v0.0.11c - 20090124 (AndersK)
*Checks for seclogon service on NT5 and returns ERROR_SERVICE_NOT_ACTIVE in $0 if not running
v0.0.11b - 20090102 (AndersK)
*Fixed unicode compile bugs
v0.0.11 - 20081021 (AndersK)
+Added UAC_GetUserShellFolderPath.nsi (Uses the new UAC::GetShellFolderPath)
v0.0.10a - 20081004 (AndersK)
+Added SEE_MASK_NOZONECHECKS flag (experimental)
v0.0.10 - 20080812 (AndersK)
+Added ugly hook hack to the shells run-as dialog on xp, defaults to other user
v0.0.9 - 20080721 (AndersK)
*Fixed UAC_RealWorldFullyLoadedDualModeExample.nsi related bug (Thanks Case)
v0.0.8 - 20080310 (AndersK)
+HTML Readme
+Added UAC::GetOuterHwnd (used by UAC_RealWorldFullyLoadedDualModeExample.nsi)
*Fixed UAC_RealWorldFullyLoadedDualModeExample.nsi
*Major code cleanup in UAC.cpp
-Removed UAC::RunElevatedAndProcessMessages (UAC::RunElevated now supports non NULL $HWNDParent)
-Removed several useless sample scripts
v0.0.7e - 20080229 (AndersK)
*Added ugly hack for hackwnd to find correct title and give us a proper taskbar so the elevation dialog does not get lost (2000,XP (This also fixed Alt-Tab icon on Vista))
*Should compile with MSVC2005 now (Thanks Case)
*More unicode fixes, this time even tested with NSIS Unicode (Only RunElevated and Exec tested)
v0.0.7d - 20080226 (AndersK)
*Fixed a couple of unicode version bugs (Unicode version still untested)
*Fixed weird XP string length bug (Thanks kfank)
v0.0.7c - 20080218 (AndersK)
*Fixed SyncVars string length bug
v0.0.7b - 20080205 (AndersK)
*Fixed DelayLoadDlls() problem on NT4
v0.0.7 - 20080120 (AndersK)
+Added UAC::StackPush (For use with ExecCodeSegment)
v0.0.6d - 20071108 (AndersK)
+Now syncs basic registers/variables before calling UAC::*Exec* and UAC::ExecCodeSegment (r0-r9,R0-R9,$CMDLINE,$INSTDIR,$OUTDIR,$EXEDIR,$LANGUAGE)
+Added UAC::RunElevatedAndProcessMessages, this can be called after .onInit (Very experimental, DO NOT USE)
+New include file with helper macros: UAC.nsh
*Replazed Clammerz hack with a better version
v0.0.6c - 20071014 (AndersK)
+Check for and split up "domain\user" style input in RunAs.cpp for CreateProcessWithLogonW
*Added a ugly hack to trick messagebox'es in .OnInit to appear correctly on Vista (Thanks Clammerz)
v0.0.6b - 20070523 (AndersK)
*Fixed showwindow flag (Thanks for the help kichik)
v0.0.6 - 20070512 (AndersK)
+Added basic language support for MyRunAs dialog.
v0.0.5e - 20070509 (AndersK)
*Fixed detection of UAC mode?
+IPC window is visible (but offscreen) during elevation to help with SetForegroundWindow/Focus problems
v0.0.5d - 20070324 (AndersK)
*Fixed stupid IsAdmin bug
v0.0.5c - 20070304 (AndersK)
*_IsAdmin now uses CheckTokenMembership if it exists ( MSKB:Q118626 / http://blogs.msdn.com/larryosterman/archive/2007/03/14/why-does-kb-118626-use-accesscheck-to-check-if-you-re-a-member-of-the-administrators-group.aspx )
v0.0.5b - 20070301 (AndersK)
*Fixed ExecCodeSegment (Thread now calls CoInitialize)
v0.0.5 - 20070228 (AndersK)
+Added ExecCodeSegment (You can now call ANY code in the context of the original user)
v0.0.4b - 20070226 (AndersK)
*Fixed (My)RunAs font (http://blogs.msdn.com/oldnewthing/archive/2005/02/04/366987.aspx)
v0.0.4 - 20070225 (AndersK)
+Added (My)RunAs dialog, used on Vista when running as LUA with UAC off
+Always uses /NCRC for elevated instance
*Now compiles as UNICODE (Untested, no UnicodeNSIS to test on)
v0.0.3 - 20070224 (AndersK)
+Added Exec/ExecWait
+Added Verb & ShowWindow support for ShellExec[Wait]
v0.0.2 - 20070219 (AndersK)
+Added ShellExecWait
*IPC srv wnd now has its own thread and msg loop
*Removed CRT dependency
*Hopefully loads on Win95 now
v0.0.1 - 20070215 (AndersK)
*Initial release
\ No newline at end of file
This software is provided 'as-is', without any express or implied warranty.
ZLIB/LIBPNG LICENSE
-------------------
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
\ No newline at end of file
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<title>UAC plug-in readme</title>
<script type="text/javascript">
function NavGL(q){window.open("http://www.google.com/search?hl=en&btnI=I&num=2&q="+escape(q));return 0;}
</script>
<style type="text/css">
html,body {background-color:#FFF; color:#000;}
a:link, a:visited, a:active {color:#00F;}
h2 {border-bottom:0.1em solid #000;}
#docHdrHdln{text-align:center;}
.importanttxt {color:#e00;}
.code {font-family:monospace;}
.nsisvar {color:#C00;}
.str {color:#390}
.inifile {background-color:#EEE;border:1px solid #000;padding:0.2em;}
.inicomment {background-color:#f5f5c5;color:#555;}
table.piexport {text-align:left;margin-bottom:1em;}
table.piexport td {vertical-align:top;}
table.piexport table.ret {padding:0;margin:0;border:0;}
</style>
</head><body>
<h1 id="docHdrHdln">UAC plug-in</h1>
<code><pre>
Interactive User (MediumIL) Admin user(HighIL)
+++[Setup.exe]++++++++++++++ +++[Setup.exe]++++++++++++++
+ + + +
+ ***[.OnInit]************ + + ***[.OnInit]************ +
+ * UAC::RunElevated >---+-+------>+ * * +
+ * NSIS.Quit() * + + * * +
+ ************************ + + ***********||*********** +
+ + + || +
+ + + \/ +
+ ***[Sections]*********** + + ***[Sections]*********** +
+ * * + /--+-+-< UAC::Exec * +
+ ************************ + | + ************************ +
+ + | + +
+ Win32.CreateProcess() <-+----/ + +
+ + + +
++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++
</pre></code>
<h2>Contents</h2>
<ul>
<li><a href="#exports">Plugin Functions</a>
<li><a href="#lang">Language support</a>
<li><a href="#knownissues">Known Issues</a>
<li><a href="#glossary">Glossary</a>
</ul>
<a name="exports"><h2>Plugin Functions</h2></a><div class="CntSec"><p>
Every function will try to emulate the basic NSIS instruction (of similar name) when UAC::RunElevated has not "succeeded" or running on a system that does not support elevation (Win9x/NT4)</p>
<table class="piexport"><tr><th colspan=2>UAC::RunElevated</th></tr>
<tr><td>Parameters:</td><td></td></tr>
<tr><td>Returns:</td><td>
<table class="ret">
<tr><td><span class="nsisvar">$0</span></td><td>Win32 error code (0 on success, 1223 if user aborted elevation dialog, anything else should be treated as a fatal error)</td></tr>
<tr><td><span class="nsisvar">$1</span></td><td><span class="code">If <span class="nsisvar">$0</span>==0</span>:
<table class="ret">
<tr><td>0</td><td>UAC is not supported by the OS</td></tr>
<tr><td>1</td><td>Started a elevated child process, the current process should act like a wrapper (Call Quit without any further processing)</td></tr>
<tr><td>2</td><td>The process is already running @ HighIL (Member of admin group)</td></tr>
<tr><td>3</td><td>You should call RunElevated again (This can happen if a user without admin priv. is used in the runas dialog)</td></tr>
</table>
</td></tr>
<tr><td><span class="nsisvar">$2</span></td><td><span class="code">If <span class="nsisvar">$0</span>==0 && <span class="nsisvar">$1</span>==1</span>: ExitCode of the elevated fork process (The NSIS errlvl is also set)</td></tr>
<tr><td><span class="nsisvar">$3</span></td><td><span class="code">If <span class="nsisvar">$0</span>==0</span>: 1 if the user is a member of the admin group or 0 otherwise</td></tr>
</table></td></tr>
<tr><td>Description:</td><td>Allows non-admin/UAC.LUA users to re-spawn the installer as another user and UAC.Admin users to elevate.</td></tr>
</table>
<!--table class="piexport"><tr><th colspan=2>UAC::RunElevatedAndProcessMessages <i style="font-size:smaller;">(Experimental)</i></th></tr>
<tr><td>Parameters:</td><td></td></tr>
<tr><td>Returns:</td><td><i>See UAC::RunElevated</i></td></tr>
<tr><td>Description:</td><td>Version of UAC::RunElevated that can be called from a page</td></tr>
</table-->
<table class="piexport"><tr><th colspan=2>UAC::Unload</th></tr>
<tr><td>Parameters:</td><td></td></tr>
<tr><td>Returns:</td><td></td></tr>
<tr><td>Description:</td><td>Cleanup, you must call this function in .OnInstFailed, .onUserAbort and .OnInstSuccess</td></tr>
</table>
<table class="piexport"><tr>
<th colspan=2>UAC::Exec</th></tr>
<tr><td>Parameters:</td><td>&lt;INT:ShowWindow&gt; &lt;STR:App&gt; &lt;STR:Parameters&gt; &lt;STR:WorkingDir&gt;</td></tr>
<tr><td>Returns:</td><td>
<table class="ret">
<tr><td><span class="nsisvar">$0</span></td><td>Win32 error code, 0 on success (ErrorFlag is also set on error)</td></tr>
</table></td></tr>
</table>
<table class="piexport"><tr>
<th colspan=2>UAC::ExecWait</th></tr>
<tr><td>Parameters:</td><td>&lt;INT:ShowWindow&gt; &lt;STR:App&gt; &lt;STR:Parameters&gt; &lt;STR:WorkingDir&gt;</td></tr>
<tr><td>Returns:</td><td>
<table class="ret">
<tr><td><span class="nsisvar">$0</span></td><td>Win32 error code, 0 on success (ErrorFlag is also set on error)</td></tr>
<tr><td><span class="nsisvar">$1</span></td><td>Exitcode of new process</td></tr>
</table></td></tr>
</table>
<table class="piexport"><tr>
<th colspan=2>UAC::ShellExec</th></tr>
<tr><td>Parameters:</td><td>&lt;STR:Verb&gt; &lt;INT:ShowWindow&gt; &lt;STR:App&gt; &lt;STR:Parameters&gt; &lt;STR:WorkingDir&gt;</td></tr>
<tr><td>Returns:</td><td>
<table class="ret">
<tr><td><span class="nsisvar">$0</span></td><td>Win32 error code, 0 on success (ErrorFlag is also set on error)</td></tr>
</table></td></tr>
</table>
<table class="piexport"><tr>
<th colspan=2>UAC::ShellExecWait</th></tr>
<tr><td>Parameters:</td><td>&lt;STR:Verb&gt; &lt;INT:ShowWindow&gt; &lt;STR:App&gt; &lt;STR:Parameters&gt; &lt;STR:WorkingDir&gt;</td></tr>
<tr><td>Returns:</td><td>
<table class="ret">
<tr><td><span class="nsisvar">$0</span></td><td>Win32 error code, 0 on success (ErrorFlag is also set on error)</td></tr>
<tr><td><span class="nsisvar">$1</span></td><td>Exitcode of new process</td></tr>
</table></td></tr>
</table>
<table class="piexport"><tr><th colspan=2>UAC::IsAdmin</th></tr>
<tr><td>Parameters:</td><td></td></tr>
<tr><td>Returns:</td><td><span class="nsisvar">$0</span> (BOOL) result</td></tr>
<tr><td>Description:</td><td>Check current thread/process token for a non-deny admin group SID entry</td></tr>
</table>
<table class="piexport"><tr><th colspan=2>UAC::ExecCodeSegment</th></tr>
<tr><td>Parameters:</td><td>&lt;INT:NSISFunctionAddress&gt;</td></tr>
<tr><td>Returns:</td><td>[None] (ErrorFlag is set on error)</td></tr>
<tr><td>Description:</td><td>Calls NSIS function in LUA/outer instance (If you use instructions that alter the UI or the stack/variables in the code segment (StrCpy,Push/Pop/Exch,DetailPrint etc.) they will affect the hidden wrapper installer and not "your" installer instance)</td></tr>
</table>
<table class="piexport"><tr><th colspan=2>UAC::StackPush</th></tr>
<tr><td>Parameters:</td><td>&lt;STR:String&gt;</td></tr>
<tr><td>Returns:</td><td>[None] (ErrorFlag is set on error)</td></tr>
<tr><td>Description:</td><td>Push to outer instance stack (For use with UAC::ExecCodeSegment)</td></tr>
</table>
<table class="piexport"><tr><th colspan=2>UAC::GetOuterHwnd</th></tr>
<tr><td>Parameters:</td><td></td></tr>
<tr><td>Returns:</td><td><span class="nsisvar">$0</span> HWNDPARENT of outer instance</td></tr>
<tr><td>Description:</td><td>For use with ${UAC.RunElevatedAndProcessMessages}</td></tr>
</table>
<table class="piexport"><tr><th colspan=2>UAC::SupportsUAC</th></tr>
<tr><td>Parameters:</td><td></td></tr>
<tr><td>Returns:</td><td><span class="nsisvar">$0</span> !=0 if supported</td></tr>
<tr><td>Description:</td><td>Check if the OS supports UAC (And the user has UAC turned on) <span class="importanttxt">This function only tests if UAC is active, will return 0 on NT5 even though runas is implemented on those platforms, will also return 0 on NT6+ if UAC is off. You should only call this function during testing, NOT to determine if you can call UAC::RunElevated</span></td></tr>
</table>
<table class="piexport"><tr><th colspan=2>UAC::GetElevationType</th></tr>
<tr><td>Parameters:</td><td></td></tr>
<tr><td>Returns:</td><td>
<table class="ret">
<tr><td><span class="nsisvar">$0</span></td><td><a href="#" OnClick="return NavGL('TOKEN_ELEVATION_TYPE Enumeration')">TOKEN_ELEVATION_TYPE</a>:
<table class="ret">
<tr><td>0</td><td>Unsupported/Failed (ErrorFlag is also set)</td></tr>
<tr><td>1</td><td>TokenElevationTypeDefault: User is not using a split token (UAC disabled)</td></tr>
<tr><td>2</td><td>TokenElevationTypeFull: UAC enabled, the (current) process is elevated</td></tr>
<tr><td>3</td><td>TokenElevationTypeLimited: UAC enabled, the process is not elevated</td></tr>
</table>
</td></tr>
</table></td></tr>
</table>
</div>
<a name="lang"><h2>Language support</h2></a><div class="CntSec">
<p>If the plugin is built with FEAT_CUSTOMRUNASDLG_TRANSLATE (Enabled by default),
you can extract a file named <span class="str">UAC.LNG</span> to <span class="nsisvar">$pluginsdir</span>.
It is a ini file with the following sections:
</p><pre class="inifile">
[MyRunAsCfg]
<span class="inicomment">;Set to 1 to disable the radio button</span>
DisableCurrUserOpt=
<span class="inicomment">;Set to 1 to hide the radio button</span>
HideCurrUserOpt=
[MyRunAsStrings]
DlgTitle=Hello There!
HelpText=Just do your thing!
<span class="inicomment">;Label for current user radio button, %s is replaced with result of GetUserNameEx(NameSamCompatible,...)</span>
OptCurrUser=Self service (%s)
OptOtherUser=Run as someone:
UserName=Who:
Pwd=PIN:
OK=Okey!
Cancel=No Way</pre>
</div>
<a name="knownissues"><h2>Known Issues</h2></a><div class="CntSec">
<ul>
<li>UACPI.KI#1: DetailPrint in outer process is ignored
<li>UACPI.KI#2: Elevation can fail if the installer is located on a remote share that requires authentication
</ul>
</div>
<a name="glossary"><h2>Glossary</h2></a><div class="CntSec">
<ul>
<li>AAM: Admin Approval Mode
<li>IL: Integrity level (Part of the new MIC/WIC security levels added to NT6)
<li>LUA: Limited/Least-privilege User Account
<li>MIC: <a href="http://en.wikipedia.org/wiki/Mandatory_Integrity_Control">Mandatory Integrity Controls</a> (Now known as WIC)
<li>UAC: User Account Control (Part of the UAP umbrella)
<li>UAP: User Account Protection
<li>WIC: <a href="http://www.securityfocus.com/infocus/1887">Windows Integrity Controls</a>
<li>Win32 error code: Standard windows error codes, ERROR_???
</ul>
</div>
</body></html>
/*
=======================
UAC helper include file
.......................
Macros starting with UAC.I should only be called from the installer and vice versa for UAC.U macros.
*/
!ifndef UAC_HDR__INC
!define UAC_HDR__INC
!include LogicLib.nsh
!define UAC.RunElevatedAndProcessMessages 'UAC::RunElevated '
!define UAC.Unload 'UAC::Unload '
!define UAC.StackPush 'UAC::StackPush '
/*!macro _UAC.BuildOnInitElevationFunc _funcprefix
Function ${_funcprefix}onInit
!ifmacrodef
FunctionEnd
!macroend*/
!macro _UAC.GenerateSimpleFunction _funcprefix _funcName _funcCode
Function ${_funcprefix}${_funcName}
${_funcCode}
#messagebox mb_ok "${_funcprefix}${_funcName}"
FunctionEnd
!macroend
!macro _UAC.TryDef _d _v
!ifndef ${_d}
!define ${_d} "${_v}"
!endif
!macroend
!macro _UAC.InitStrings _modeprefix
!insertmacro _UAC.TryDef UACSTR.UnDataFile "UAC.dat"
!insertmacro _UAC.TryDef UACSTR.${_modeprefix}ElvWinErr "Unable to elevate , error $0"
!ifNdef __UNINSTALL__
!insertmacro _UAC.TryDef UACSTR.${_modeprefix}ElvAbortReqAdmin "This installer requires admin access, aborting!"
!insertmacro _UAC.TryDef UACSTR.${_modeprefix}ElvMustTryAgain "This installer requires admin access, try again"
!else
!insertmacro _UAC.TryDef UACSTR.${_modeprefix}ElvAbortReqAdmin "This uninstaller requires admin access, aborting!"
!insertmacro _UAC.TryDef UACSTR.${_modeprefix}ElvMustTryAgain "This uninstaller requires admin access, try again"
!endif
!macroend
!ifmacroNdef _UAC.GenerateUninstallerTango
!macro _UAC.GenerateUninstallerTango UninstallerFileName
!ifdef __GLOBAL__
!error "UAC: Needs to be called inside a function"
!endif
!ifNdef __UNINSTALL__
!error "UAC: _UAC.GenerateUninstallerTango should only be called by uninstaller, see http://forums.winamp.com/showthread.php?threadid=280330"
!endif
!ifNdef UAC_UNINSTALLERTANGOFORALLPLATFORMS
!include WinVer.nsh
!endif
!insertmacro _UAC.InitStrings 'U.'
ReadIniStr $0 "$ExeDir\${UACSTR.UnDataFile}" UAC "Un.Ready"
${IF} $0 != 1
!ifNdef UAC_UNINSTALLERTANGOFORALLPLATFORMS
${AndIf} ${AtLeastWinVista}
!endif
InitPluginsDir
WriteIniStr "$PluginsDir\${UACSTR.UnDataFile}" UAC "Un.Ready" 1
CopyFiles /SILENT "$EXEPATH" "$PluginsDir\${UninstallerFileName}"
StrCpy $0 ""
${IfThen} ${Silent} ${|} StrCpy $0 "/S " ${|}
ExecWait '"$PluginsDir\${UninstallerFileName}" $0/NCRC _?=$INSTDIR' $0
SetErrorLevel $0
Quit
${EndIf}
!macroend
!endif
!ifmacroNdef _UAC.GenerateOnInitElevationCode
!macro _UAC.GenerateOnInitElevationCode _modeprefix
!ifndef __FUNCTION__
!error "UAC: Needs to be called inside a function"
!endif
!insertmacro _UAC.InitStrings ${_modeprefix}
!define _UAC.GOIECUniq L${__LINE__}
UAC_Elevate_${_UAC.GOIECUniq}:
UAC::RunElevated
StrCmp 1223 $0 UAC_ElevationAborted_${_UAC.GOIECUniq} ; UAC dialog aborted by user?
StrCmp 0 $0 0 UAC_Err_${_UAC.GOIECUniq} ; Error?
StrCmp 1 $1 0 UAC_Success_${_UAC.GOIECUniq} ;Are we the real deal or just the wrapper?
Quit
UAC_Err_${_UAC.GOIECUniq}:
MessageBox mb_iconstop "${UACSTR.${_modeprefix}ElvWinErr}"
Abort
UAC_ElevationAborted_${_UAC.GOIECUniq}:
MessageBox mb_iconstop "${UACSTR.${_modeprefix}ElvAbortReqAdmin}"
Abort
UAC_Success_${_UAC.GOIECUniq}:
# if $0==0 && $3==1, we are a member of the admin group (Any OS)
# if $0==0 && $1==0, UAC not supported (Probably <NT6), run as normal?
# if $0==0 && $1==3, we can try to elevate again
StrCmp 1 $3 /*+4*/ UAC_Done_${_UAC.GOIECUniq} ;Admin?
StrCmp 3 $1 0 UAC_ElevationAborted_${_UAC.GOIECUniq} ;Try again or abort?
MessageBox mb_iconexclamation "${UACSTR.${_modeprefix}ElvMustTryAgain}" ;Inform user...
goto UAC_Elevate_${_UAC.GOIECUniq} ;...lets try again
UAC_Done_${_UAC.GOIECUniq}:
!undef _UAC.GOIECUniq
!macroend
!endif
!define UAC.I.Elevate.AdminOnly '!insertmacro UAC.I.Elevate.AdminOnly '
!macro UAC.I.Elevate.AdminOnly
!insertmacro _UAC.GenerateOnInitElevationCode 'I.'
!macroend
!define UAC.U.Elevate.AdminOnly '!insertmacro UAC.U.Elevate.AdminOnly '
!macro UAC.U.Elevate.AdminOnly _UninstallerName
!ifNdef UAC_DISABLEUNINSTALLERTANGO
!insertmacro _UAC.GenerateUninstallerTango "${_UninstallerName}"
!endif
!insertmacro _UAC.GenerateOnInitElevationCode 'U.'
!macroend
!define UAC.AutoCodeUnload '!insertmacro UAC.AutoCodeUnload '
!macro UAC.AutoCodeUnload _HasUninstaller
!insertmacro _UAC.GenerateSimpleFunction "" .OnInstFailed '${UAC.Unload}'
!insertmacro _UAC.GenerateSimpleFunction "" .OnInstSuccess '${UAC.Unload}'
!ifNdef MUI_INCLUDED
!insertmacro _UAC.GenerateSimpleFunction "" .onUserAbort '${UAC.Unload}'
!else
!ifNdef MUI_CUSTOMFUNCTION_ABORT
!error "UAC: must call $$ {UAC.Unload} in MUI_CUSTOMFUNCTION_ABORT!"
!endif
!endif
!if "${_HasUninstaller}" != ""
!insertmacro _UAC.GenerateSimpleFunction "un" .onUninstFailed '${UAC.Unload}'
!insertmacro _UAC.GenerateSimpleFunction "un" .onUninstSuccess '${UAC.Unload}'
!ifNdef MUI_INCLUDED
!insertmacro _UAC.GenerateSimpleFunction "un" .onUserAbort '${UAC.Unload}'
!else
!ifNdef MUI_CUSTOMFUNCTION_ABORT
!error "UAC: must call $$ {UAC.Unload} in MUI_CUSTOMFUNCTION_(UN)ABORT!"
!endif
!endif
!endif
!macroend
!define UAC.FastCallFunctionAsUser '!insertmacro UAC.FastCallFunctionAsUser '
!macro UAC.FastCallFunctionAsUser _func _var
GetFunctionAddress ${_var} ${_func}
UAC::ExecCodeSegment ${_var}
!macroend
!define UAC.CallFunctionAsUser '!insertmacro UAC.CallFunctionAsUser '
!macro UAC.CallFunctionAsUser _func
push $R9
!insertmacro UAC.FastCallFunctionAsUser ${_func} $R9
pop $R9
!macroend
!define UAC.FastCallGetOuterInstanceHwndParent UAC::GetOuterHwnd
!define UAC.GetOuterInstanceHwndParent '!insertmacro UAC.GetOuterInstanceHwndParent '
!macro UAC.GetOuterInstanceHwndParent _var
push $0
${UAC.FastCallGetOuterInstanceHwndParent}
Exch $0
Pop ${_var}
!macroend
!macro _UAC.DumpEx _disp _f _fp _v
${_f} ${_fp}
DetailPrint "${_disp}=${_v}"
!macroend
!macro _UAC.Dump _f _fp _v
!insertmacro _UAC.DumpEx `${_f}` `${_f}` `${_fp}` `${_v}`
!macroend
!macro _UAC.DbgDetailPrint
push $0
push $1
System::Call /NoUnload "advapi32::GetUserName(t.r0,*i${NSIS_MAX_STRLEN})"
System::Call "Kernel32::GetComputerName(t.r1,*i${NSIS_MAX_STRLEN})"
DetailPrint "$1\$0"
;!insertmacro _UAC.DumpEx "User" System::Call "advapi32::GetUserName(t.r0,*i${NSIS_MAX_STRLEN})" $0
!insertmacro _UAC.DumpEx "CmdLine" "" "" "$CmdLine"
!insertmacro _UAC.Dump UAC::IsAdmin "" $0
!insertmacro _UAC.Dump UAC::SupportsUAC "" $0
!insertmacro _UAC.Dump UAC::GetElevationType "" $0
pop $1
pop $0
!macroend
!endif /* ifndef UAC_HDR__INC */
\ No newline at end of file
RequestExecutionLevel user /* RequestExecutionLevel REQUIRED! */
!define APPNAME "UAC_AdminOnly"
Name "${APPNAME}"
OutFile "${APPNAME}.exe"
ShowInstDetails show
!include UAC.nsh ;<<< New headerfile that does everything for you ;)
!include LogicLib.nsh
!define UACSTR.I.ElvAbortReqAdmin "This fancy app requires admin rights fool" ;custom error string, see _UAC.InitStrings macro in uac.nsh for more
Function .OnInit
${UAC.I.Elevate.AdminOnly}
FunctionEnd
Function .OnInstFailed
${UAC.Unload}
FunctionEnd
Function .OnInstSuccess
${UAC.Unload}
FunctionEnd
Function ExecCodeSegmentTest
${If} "$1" != "666, the # of the beast"
MessageBox mb_ok "uh oh"
${EndIf}
FunctionEnd
Section "Info"
!insertmacro _UAC.DbgDetailPrint
StrCpy $1 "666, the # of the beast"
!insertmacro UAC.CallFunctionAsUser ExecCodeSegmentTest
SectionEnd
page InstFiles
/* LEGACY CODE: (now uses magic code from UAC.nsh)
Function .OnInit
UAC_Elevate:
UAC::RunElevated
StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user?
StrCmp 0 $0 0 UAC_Err ; Error?
StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper?
Quit
UAC_Err:
MessageBox mb_iconstop "Unable to elevate , error $0"
Abort
UAC_ElevationAborted:
/*System::Call "user32::CreateWindowEx(i ${WS_EX_TRANSPARENT}|${WS_EX_LAYERED}, t 'Button', t 'blah', i 0, i 10, i 10, i 10, i 10, i 0, i 0, i 0) i .r0"
ShowWindow $0 ${SW_SHOW}
System::Call "user32::SetForegroundWindow(i r0) i."
System::Call "user32::DestroyWindow(i r0) i."
* /
MessageBox mb_iconstop "This installer requires admin access, aborting!"
Abort
UAC_Success:
StrCmp 1 $3 +4 ;Admin?
StrCmp 3 $1 0 UAC_ElevationAborted ;Try again or abort?
MessageBox mb_iconstop "This installer requires admin access, try again" ;Inform user...
goto UAC_Elevate ;... and try again
FunctionEnd*/
\ No newline at end of file
/*
This sample will try to elevate, but it will also allow non admin users to continue if they click cancel in the elevation dialog
*/
RequestExecutionLevel user /* RequestExecutionLevel REQUIRED! */
!define APPNAME "UAC_AllowLUA"
Name "${APPNAME}"
OutFile "${APPNAME}.exe"
ShowInstDetails show
!include UAC.nsh
Function .OnInstFailed
UAC::Unload ;Must call unload!
FunctionEnd
Function .OnInstSuccess
UAC::Unload ;Must call unload!
FunctionEnd
Function .OnInit
UAC::RunElevated
;MessageBox mb_iconinformation "Debug: UAC::RunElevated: $\n0(Error)=$0 $\n1(UACMode)=$1 $\n2=$2 $\nadmin=$3$\n$\n$CmdLine"
StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user?
StrCmp 0 $0 0 UAC_Err ; Error?
StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper?
Quit
UAC_Err:
MessageBox mb_iconstop "Unable to elevate , error $0"
Abort
UAC_ElevationAborted:
# elevation was aborted, we still run as normal
UAC_Success:
FunctionEnd
Section "Info"
!insertmacro _UAC.DbgDetailPrint
SectionEnd
Page InstFiles
RequestExecutionLevel user /* RequestExecutionLevel REQUIRED! */
!define APPNAME "UAC_GetUserShellFolderPath"
Name "${APPNAME}"
OutFile "${APPNAME}.exe"
ShowInstDetails show
!include UAC.nsh
!include LogicLib.nsh
page instfiles
Function .onInit
${UAC.I.Elevate.AdminOnly}
FunctionEnd
!ifndef CSIDL_PERSONAL
!define CSIDL_PERSONAL 0x0005 ;My Documents
!endif
Section
/*
You can specify a fallback value in the 2nd parameter, it is used if the installer is not elevated
or running on NT4/Win9x or on errors.
If you just want to check for success, use "" as the 2nd parameter and compare $0 with ""
*/
UAC::GetShellFolderPath ${CSIDL_PERSONAL} $Documents
DetailPrint MyDocs=$0
SectionEnd
/*
This sample supports two modes, installing as a normal user (single user install) AND as admin (all users install)
This sample uses the registry plugin, so you need to download that if you don't already have it
*/
!define APPNAME "UAC_RealWorldFullyLoadedDualMode"
!define ELEVATIONTITLE "${APPNAME}: Elevate" ;displayed during elevation on our custom page
!define SMSUBDIR $StartMenuFolder ;"${APPNAME}"
!define UNINSTALLER_NAME "Uninstall ${APPNAME}.exe"
!define UNINSTALLER_REGSECTION "${APPNAME}"
!define RegPath.MSUninstall "Software\Microsoft\Windows\CurrentVersion\Uninstall"
Name "${APPNAME}"
OutFile "${APPNAME}.exe"
ShowInstDetails show
SetCompressor LZMA
RequestExecutionLevel user /* RequestExecutionLevel REQUIRED! */
!include MUI.nsh
!include UAC.nsh
!include LogicLib.nsh
!include Registry.nsh
!include nsDialogs.nsh ;for our custom page
!include FileFunc.nsh ;we need to parse the command line
!insertmacro GetParameters
!insertmacro GetOptions
!define MUI_CUSTOMFUNCTION_ABORT onAbort
!define MUI_CUSTOMFUNCTION_GUIINIT onGuiInit
!define MUI_COMPONENTSPAGE_NODESC
!define MUI_FINISHPAGE_NOAUTOCLOSE
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\llama-blue.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\llama-blue.ico"
!define MUI_WELCOMEPAGE_TITLE_3LINES
var InstMode # 0: Single user, 1:All users, >1:elevated instance, perform page jump
var hKey # Reg hive
var hSelModeAdminRadio
var StartMenuFolder
!macro SetMode IsAdmin
!if "${IsAdmin}" > 0
SetShellVarContext all
StrCpy $InstMode 1
StrCpy $hKey HKLM
!else
SetShellVarContext current
StrCpy $InstMode 0
StrCpy $hKey HKCU
!endif
!macroend
Function .OnInit
!insertmacro SetMode 0
${GetParameters} $R9
${GetOptions} "$R9" UAC $0 ;look for special /UAC:???? parameter (sort of undocumented)
${Unless} ${Errors}
UAC::IsAdmin
${If} $0 < 1
SetErrorLevel 666 ;special return value for outer instance so it knows we did not have admin rights
Quit
${EndIf}
!insertmacro SetMode 1
StrCpy $InstMode 2
${EndIf}
FunctionEnd
Function onGuiInit
${If} $InstMode >= 2
${UAC.GetOuterInstanceHwndParent} $0
${If} $0 <> 0
System::Call /NOUNLOAD "*(i,i,i,i)i.r1"
System::Call /NOUNLOAD 'user32::GetWindowRect(i $0,i r1)i.r2'
${If} $2 <> 0
System::Call /NOUNLOAD "*$1(i.r2,i.r3)"
System::Call /NOUNLOAD 'user32::SetWindowPos(i $hwndParent,i0,ir2,ir3,i0,i0,i 4|1)'
${EndIf}
ShowWindow $hwndParent ${SW_SHOW}
ShowWindow $0 ${SW_HIDE} ;hide outer instance installer window
System::Free $1
${EndIf}
${EndIf}
FunctionEnd
Function Un.OnInit
!insertmacro SetMode 0
ReadRegDWORD $0 HKLM "${RegPath.MSUninstall}\${UNINSTALLER_REGSECTION}" InstMode ;We saved the "mode" in the installer
${If} $0 U> 0
; If it was installed for all users, we have to be admin to uninstall it
${UAC.U.Elevate.AdminOnly} "${UNINSTALLER_NAME}"
!insertmacro SetMode 1
${EndIf}
FunctionEnd
Function onAbort
${UAC.Unload}
FunctionEnd
${UAC.AutoCodeUnload} 1 ;Auto-generate .OnInstFailed and .OnInstSuccess functions
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipPageInElvModePreCB
!insertmacro MUI_PAGE_WELCOME
Page Custom ModeSelectionPageCreate ModeSelectionPageLeave
!define MUI_PAGE_CUSTOMFUNCTION_PRE CmpntsPreCB
!insertmacro MUI_PAGE_COMPONENTS
!define MUI_PAGE_CUSTOMFUNCTION_PRE DirPreCB
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_STARTMENU 1 $StartMenuFolder
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_TITLE_3LINES
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION FinishRunCB
!insertmacro MUI_PAGE_FINISH
!define MUI_WELCOMEPAGE_TITLE_3LINES
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
Function CmpntsPreCB
GetDlgItem $0 $hwndparent 3
${IfThen} $InstMode >= 1 ${|} EnableWindow $0 0 ${|} ;prevent user from going back and selecting single user so noobs don't end up installing as the wrong user
FunctionEnd
Function SkipPageInElvModePreCB
${IfThen} $InstMode > 1 ${|} Abort ${|} ;skip this page so we get to the mode selection page
FunctionEnd
Function ModeSelectionPageCreate
${If} $InstMode > 1
StrCpy $InstMode 1
Abort ;skip this page and contine where the "parent" would have gone
${EndIf}
!insertmacro MUI_HEADER_TEXT_PAGE "Select install type" "Blah blah blah blah"
nsDialogs::Create /NOUNLOAD 1018
Pop $0
${NSD_CreateLabel} 0 20u 75% 20u "Blah blah blah blah select install type..."
Pop $0
System::Call "advapi32::GetUserName(t.r0, *i ${NSIS_MAX_STRLEN}r1) i.r2"
${NSD_CreateRadioButton} 0 40u 75% 15u "Single User ($0)"
Pop $0
${IfThen} $InstMode U< 1 ${|} SendMessage $0 ${BM_SETCHECK} 1 0 ${|}
${NSD_CreateRadioButton} 0 60u 75% 15u "All users"
Pop $hSelModeAdminRadio
${IfThen} $InstMode U> 0 ${|} SendMessage $hSelModeAdminRadio ${BM_SETCHECK} 1 0 ${|}
nsDialogs::Show
FunctionEnd
!macro EnableCtrl dlg id state
push $language
GetDlgItem $language ${dlg} ${id}
EnableWindow $language ${state}
pop $language
!macroend
Function ModeSelectionPageLeave
SendMessage $hSelModeAdminRadio ${BM_GETCHECK} 0 0 $9
UAC::IsAdmin
${If} $9 U> 0
${If} $0 <> 0
!insertmacro SetMode 1
${Else}
System::Call /NoUnload 'user32::GetWindowText(i $HwndParent,t.R1,i ${NSIS_MAX_STRLEN})' ;get original window title
System::Call /NoUnload 'user32::SetWindowText(i $HwndParent,t "${ELEVATIONTITLE}")' ;set out special title
StrCpy $2 "" ;reset special return, only gets set when sub process is executed, not when user cancels
!insertmacro EnableCtrl $HWNDParent 1 0 ;Disable next button, just because it looks good ;)
${UAC.RunElevatedAndProcessMessages}
!insertmacro EnableCtrl $HWNDParent 1 1
System::Call 'user32::SetWindowText(i $HwndParent,t "$R1")' ;restore title
${If} $2 = 666 ;our special return, the new process was not admin after all
MessageBox mb_iconExclamation "You need to login with an account that is a member of the admin group to continue..."
Abort
${ElseIf} $0 = 1223 ;cancel
Abort
${Else}
${If} $0 <> 0
${If} $0 = 1062
MessageBox mb_iconstop "Unable to elevate, Secondary Logon service not running!"
${Else}
MessageBox mb_iconstop "Unable to elevate, error $0 ($1,$2,$3)"
${EndIf}
Abort
${EndIf}
${EndIf}
Quit ;We now have a new process, the install will continue there, we have nothing left to do here
${EndIf}
${EndIf}
FunctionEnd
Function DirPreCB
${If} $InstDir == ""
${If} $InstMode U> 0
StrCpy $InstDir "$ProgramFiles\${APPNAME}"
${Else}
StrCpy $InstDir "$APPDATA\${APPNAME}"
${EndIf}
${EndIf}
FunctionEnd
Function FinishRunCB
UAC::Exec "" "Notepad.exe" "$Windir\Win.INI" "$InstDir"
FunctionEnd
Function CreateSMShortcuts
StrCpy ${SMSUBDIR} $9 ;stupid sync
CreateDirectory "$SMPrograms\${SMSUBDIR}"
CreateShortcut "$SMPrograms\${SMSUBDIR}\${APPNAME}.lnk" "$Windir\Notepad.exe"
CreateShortcut "$SMPrograms\${SMSUBDIR}\Uninstall ${APPNAME}.lnk" "$InstDir\${UNINSTALLER_NAME}"
FunctionEnd
Function CreateDeskShortcuts
CreateShortcut "$Desktop\${APPNAME}.lnk" "$Windir\Notepad.exe"
FunctionEnd
Section "!Required files"
SectionIn RO
SetOutPath -
!insertmacro _UAC.DbgDetailPrint ;some debug info, useful during testing
;Install files here...
WriteUninstaller "$InstDir\${UNINSTALLER_NAME}"
${registry::Write} "$hKey\${RegPath.MSUninstall}\${UNINSTALLER_REGSECTION}" DisplayName "${APPNAME}" REG_SZ $0
${registry::Write} "$hKey\${RegPath.MSUninstall}\${UNINSTALLER_REGSECTION}" UninstallString "$InstDir\${UNINSTALLER_NAME}" REG_SZ $0
${registry::Write} "$hKey\${RegPath.MSUninstall}\${UNINSTALLER_REGSECTION}" InstMode $InstMode REG_DWORD $0
${registry::Unload}
SectionEnd
Section "Startmenu Shortcuts"
StrCpy $9 ${SMSUBDIR} ;this is stupid as hell, we need correct ${SMSUBDIR} in the outer process, this is the only way (plugins cannot enum "custom" var's AFAIK)
${UAC.CallFunctionAsUser} CreateSMShortcuts
SectionEnd
Section "Desktop Shortcut"
${UAC.CallFunctionAsUser} CreateDeskShortcuts
SectionEnd
Section Uninstall
Delete "$InstDir\${UNINSTALLER_NAME}"
Delete "$SMPrograms\${SMSUBDIR}\${APPNAME}.lnk"
Delete "$SMPrograms\${SMSUBDIR}\Uninstall ${APPNAME}.lnk"
RMDir "$SMPrograms\${SMSUBDIR}"
Delete "$Desktop\${APPNAME}.lnk"
RMDir "$InstDir"
${registry::DeleteKey} "$hKey\${RegPath.MSUninstall}\${UNINSTALLER_REGSECTION}" $0
${registry::Unload}
SectionEnd
\ No newline at end of file
/*
This script was made in response to http://forums.winamp.com/showthread.php?threadid=280330
It is a ugly hack and is mostly here just to have a solution right now.
Hopefully, NSIS will add support for changing the RequestExecutionLevel of the uninstaller
This code inspired the _UAC.GenerateUninstallerTango macro (called by ${UAC.U.Elevate.AdminOnly} unless you define UAC_DISABLEUNINSTALLERTANGO)
*/
RequestExecutionLevel user /* RequestExecutionLevel REQUIRED! */
!define APPNAME "UAC_Uninstaller"
Name "${APPNAME}"
OutFile "${APPNAME}.exe"
ShowInstDetails show
!include LogicLib.nsh
!define UNINSTALLER_UACDATA "uac.ini"
!define UNINSTALLER_NAME "Uninstall FooBarBaz"
Function un.onInit
ReadIniStr $0 "$ExeDir\${UNINSTALLER_UACDATA}" UAC "Un.First"
${IF} $0 != 1
;SetSilent silent
InitPluginsDir
WriteIniStr "$PluginsDir\${UNINSTALLER_UACDATA}" UAC "Un.First" 1
CopyFiles /SILENT "$EXEPATH" "$PluginsDir\${UNINSTALLER_NAME}.exe"
StrCpy $0 ""
${IfThen} ${Silent} ${|} StrCpy $0 "/S " ${|}
ExecWait '"$PluginsDir\${UNINSTALLER_NAME}.exe" $0/NCRC _?=$INSTDIR' $0
SetErrorLevel $0
Quit
${EndIf}
# UAC code goes here ...
FunctionEnd
Section
WriteUninstaller "$exedir\${UNINSTALLER_NAME}.exe"
SetAutoClose true
DetailPrint "Uninstalling..."
Sleep 1111
Exec '"$exedir\${UNINSTALLER_NAME}.exe"'
SectionEnd
Section uninstall
MessageBox mb_ok "My filename is: $EXEFILE"
Delete "$instdir\${UNINSTALLER_NAME}.exe"
Delete "$instdir\${APPNAME}.exe" ;delete generated installer aswell, this is just a sample script
SectionEnd
page InstFiles
\ No newline at end of file
/*
Alternative to ExDll.h
v0.0.1 - 20060811 (AndersK)
*/
#pragma once
#include <tchar.h>
typedef TCHAR NSISCH;
#define NSISCALL __stdcall
namespace NSIS {
__forceinline void* NSISCALL MemAlloc(SIZE_T cb) {return GlobalAlloc(LPTR,cb);}
__forceinline void NSISCALL MemFree(void* p) {GlobalFree(p);}
enum {
INST_0, // $0
INST_1, // $1
INST_2, // $2
INST_3, // $3
INST_4, // $4
INST_5, // $5
INST_6, // $6
INST_7, // $7
INST_8, // $8
INST_9, // $9
INST_R0, // $R0
INST_R1, // $R1
INST_R2, // $R2
INST_R3, // $R3
INST_R4, // $R4
INST_R5, // $R5
INST_R6, // $R6
INST_R7, // $R7
INST_R8, // $R8
INST_R9, // $R9
INST_CMDLINE, // $CMDLINE
INST_INSTDIR, // $INSTDIR
INST_OUTDIR, // $OUTDIR
INST_EXEDIR, // $EXEDIR
INST_LANG, // $LANGUAGE
__INST_LAST,
VIDX_TEMP=(INST_LANG+1), //#define state_temp_dir g_usrvars[25]
VIDX_PLUGINSDIR,//# define state_plugins_dir g_usrvars[26]
VIDX_EXEPATH,//#define state_exe_path g_usrvars[27]
VIDX_EXEFILENAME,//#define state_exe_file g_usrvars[28]
VIDX_STATECLICKNEXT,//#define state_click_next g_usrvars[30]
__VIDX_UNDOCLAST
};
typedef struct _stack_t {
struct _stack_t *next;
NSISCH text[ANYSIZE_ARRAY];
} stack_t;
typedef struct {
int autoclose;
int all_user_var;
int exec_error;
int abort;
int exec_reboot;
int reboot_called;
int XXX_cur_insttype; // deprecated
int XXX_insttype_changed; // deprecated
int silent;
int instdir_error;
int rtl;
int errlvl;
//NSIS v2.3x ?
int alter_reg_view;
int status_update;
} exec_flags_type;
typedef struct {
exec_flags_type *exec_flags;
int (NSISCALL *ExecuteCodeSegment)(int, HWND);
void (NSISCALL *validate_filename)(char *);
} extra_parameters;
extern UINT StrSize;
extern stack_t **StackTop;
extern NSISCH*Vars;
inline bool NSISCALL SetErrLvl(extra_parameters*pExtraParams,int ErrLevel) {return pExtraParams? ((pExtraParams->exec_flags->errlvl=ErrLevel)||true):false;}
inline bool NSISCALL SetErrorFlag(extra_parameters*pExtraParams) {return pExtraParams? ((pExtraParams->exec_flags->exec_error=1)||true):false;}
inline bool NSISCALL ClearErrorFlag(extra_parameters*pExtraParams) {return pExtraParams?((pExtraParams->exec_flags->exec_error=0)||true):false;}
__forceinline int NSISCALL ExecuteCodeSegment(extra_parameters*pExtraParams,int pos,HWND hwndProgress=NULL) {
return pExtraParams?pExtraParams->ExecuteCodeSegment(pos,hwndProgress):(/*EXEC_ERROR*/0x7FFFFFFF);
}
static NSISCH* __fastcall GetVar(const int varnum)
{
//ASSERT(NSIS::Vars && NSIS::StrSize);
if (varnum < 0 || varnum >= __VIDX_UNDOCLAST) return NULL;
return NSIS::Vars+(varnum*NSIS::StrSize);
}
inline void NSISCALL SetVarUINT(const int varnum,UINT Value) {
wsprintf(GetVar(varnum),_T("%u"),Value);
}
static stack_t* NSISCALL StackPop() {
if (NSIS::StackTop && *NSIS::StackTop) {
stack_t*s=(*NSIS::StackTop);
*NSIS::StackTop=(*NSIS::StackTop)->next;
return s;
}
return 0;
}
__forceinline void NSISCALL StackFreeItem(stack_t*pStackItem) {NSIS::MemFree(pStackItem);}
static DWORD NSISCALL StackPush(NSISCH*InStr,UINT StackStrSize=NSIS::StrSize) {
if (!NSIS::StackTop)return ERROR_INVALID_PARAMETER;
stack_t*sNew=(stack_t*)NSIS::MemAlloc(sizeof(stack_t)+(StackStrSize*sizeof(NSISCH)));
if (!sNew)return ERROR_OUTOFMEMORY;
lstrcpyn(sNew->text,InStr,StackStrSize);
sNew->next=*NSIS::StackTop;
*NSIS::StackTop=sNew;
return NO_ERROR;
}
}; /* namespace */
#define NSISUTIL_INIT() namespace NSIS {UINT StrSize;stack_t **StackTop;NSISCH*Vars;}//Call in only ONE source file
#define NSISUTIL_INITEXPORT(_v,_strsize,_stackt) NSIS::Vars=_v;NSIS::StrSize=_strsize;NSIS::StackTop=_stackt
//#define NSISEXPORT4(_func,_h,_strsize,_v,_stackt) extern "C" void __declspec(dllexport) __cdecl \
// _func (HWND _h,int _strsize,NSISCH*_v,NSIS::stack_t **_stackt) { NSISUTIL_INITEXPORT(_v,_strsize,_stackt); TRACE("EXPORT::" #_func "\n");
//#define NSISEXPORT5(_func,_h,_strsize,_v,_stackt,_eparams) extern "C" void __declspec(dllexport) __cdecl \
// _func (HWND _h,int _strsize,NSISCH*_v,NSIS::stack_t **_stackt,NSIS::extra_parameters* _eparams) { NSISUTIL_INITEXPORT(_v,_strsize,_stackt); TRACE("EXPORT::" #_func "\n");
//#define NSISEXPORT NSISEXPORT5
#define EXPORTNSISFUNC extern "C" void __declspec(dllexport) __cdecl
#define NSISFUNCSTART4(_h,_strsize,_v,_stackt) {NSISUTIL_INITEXPORT(_v,_strsize,_stackt);
#define NSISFUNCSTART5(_h,_strsize,_v,_stackt,_eparams) NSISFUNCSTART4(_h,_strsize,_v,_stackt)
#define NSISFUNCSTART NSISFUNCSTART5
#define NSISFUNCEND() }
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by resource.rc
//
#define IDD_MYRUNAS 101
#define IDC_RUNASCURR 1000
#define IDC_RUNASSPEC 1001
#define IDC_SHICON 1002
#define IDC_HELPTEXT 1003
#define IDC_USERNAME 1004
#define IDC_PASSWORD 1005
#define IDC_LBLUSER 1007
#define IDC_LBLPWD 1008
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1009
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
#include "uac.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_MYRUNAS DIALOG DISCARDABLE 0, 0, 250, 145
STYLE DS_MODALFRAME | DS_NOIDLEMSG | DS_SETFOREGROUND | DS_FIXEDSYS |
DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
FONT 8, "MS Shell Dlg"
BEGIN
DEFPUSHBUTTON "&OK",IDOK,132,122,50,14
PUSHBUTTON "Ca&ncel",IDCANCEL,188,122,50,14
ICON "",IDC_SHICON,7,7,20,20
LTEXT "",IDC_HELPTEXT,34,7,204,35
CONTROL "",IDC_RUNASCURR,"Button",BS_AUTORADIOBUTTON,20,49,218,
10
CONTROL "",IDC_RUNASSPEC,"Button",BS_AUTORADIOBUTTON,20,65,218,
10
LTEXT "&User name:",IDC_LBLUSER,20,84,42,16
EDITTEXT IDC_USERNAME,63,83,175,14,ES_AUTOHSCROLL
LTEXT "&Password:",IDC_LBLPWD,20,102,42,20
EDITTEXT IDC_PASSWORD,63,100,175,14,ES_PASSWORD | ES_AUTOHSCROLL
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
IDD_MYRUNAS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 238
TOPMARGIN, 7
BOTTOMMARGIN, 136
END
END
#endif // APSTUDIO_INVOKED
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"#include ""uac.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
//Copyright (C) 2007 Anders Kjersem. Licensed under the zlib/libpng license, see License.txt for details.
/*
If UAC is disabled, the runas verb is broken (Vista RTM) so when running as LUA there is no way to elevate so
we provide our own dialog.
*/
#include "uac.h"
#ifdef FEAT_CUSTOMRUNASDLG
#include <lmcons.h>//UNLEN && GNLEN && PWLEN
#include <windowsx.h>
#include "resource.h"
#include "nsisutil.h"
using namespace NSIS;
#define ERRAPP_TRYAGAIN (0x20000000|1)
#define max(a, b) ((a > b) ? a : b)
#define MYMAX_DOMAIN (2+max(GNLEN,MAX_COMPUTERNAME_LENGTH)+1)
static LPCTSTR g_RunAsDlgTitle=_T("Run as");
static LPCTSTR g_RunAsHelpText=_T("You may not have the necessary permissions to use all the features of the program you are about to run. You may run this program as a different user or continue to run the program as the current user.");
static LPCTSTR g_RunAsCurrUsrFmt=_T("&Current user (%s)");//Max 50 chars!
static LPCTSTR g_RunAsSpecHelp=_T("Run the program as the &following user:");
FORCEINLINE bool MySetDlgItemText(HWND hDlg,int id,LPCTSTR s) {return MySndDlgItemMsg(hDlg,id,WM_SETTEXT,0,(LPARAM)s)!=0;}
typedef struct {
SHELLEXECUTEINFO*pSEI;
bool AsSelf;
} RUNASDLGDATA;
void MyRunAsFmtCurrUserRadio(HWND hDlg,LPCTSTR Fmt) {
TCHAR bufFullName[MYMAX_DOMAIN+UNLEN+1];
TCHAR buf[50+MYMAX_DOMAIN+UNLEN+1];
*bufFullName=0;
ULONG cch;
if ((!_GetUserNameEx || !_GetUserNameEx(NameSamCompatible,bufFullName,&(cch=COUNTOF(bufFullName)))) &&
!_GetUserName(bufFullName,&(cch=COUNTOF(bufFullName))) ) {
*bufFullName=0;
}
wsprintf(buf,Fmt,*bufFullName?bufFullName:_T("?"));
MySetDlgItemText(hDlg,IDC_RUNASCURR,buf);
// default the "User name:" to Administrator from shell32
if (LoadString(GetModuleHandle(_T("SHELL32.dll")),21763, bufFullName, COUNTOF(bufFullName)) > 0) {
MySetDlgItemText(hDlg,IDC_USERNAME,bufFullName);
}
}
#ifdef FEAT_CUSTOMRUNASDLG_TRANSLATE
void MyRunAsTranslateDlgString(LPCTSTR StrID,LPTSTR Ini,HWND hDlg,INT_PTR DlgItemId,int special=0) {
TCHAR buf[MAX_PATH*2];
DWORD len=GetPrivateProfileString(_T("MyRunAsStrings"),StrID,0,buf,ARRAYSIZE(buf),Ini);
if (len) {
if (IDC_RUNASCURR==special)
MyRunAsFmtCurrUserRadio(hDlg,buf);
else
(DlgItemId==-1) ? SetWindowText(hDlg,buf) : MySetDlgItemText(hDlg,DlgItemId,buf);
}
}
void MyRunAsTranslateDlg(HWND hDlg) {
DWORD len;
TCHAR buf[MAX_PATH*2];
HMODULE hDll=GetWindowInstance(hDlg);ASSERT(hDll);
if ( (len=GetModuleFileName(hDll,buf,ARRAYSIZE(buf))) <1)return;
buf[len-3]=0;
lstrcat(buf,_T("lng"));
MyRunAsTranslateDlgString(_T("DlgTitle"),buf,hDlg,-1);
MyRunAsTranslateDlgString(_T("HelpText"),buf,hDlg,IDC_HELPTEXT);
MyRunAsTranslateDlgString(_T("OptCurrUser"),buf,hDlg,IDC_RUNASCURR,IDC_RUNASCURR);
MyRunAsTranslateDlgString(_T("OptOtherUser"),buf,hDlg,IDC_RUNASSPEC);
MyRunAsTranslateDlgString(_T("Username"),buf,hDlg,IDC_LBLUSER);
MyRunAsTranslateDlgString(_T("Pwd"),buf,hDlg,IDC_LBLPWD);
MyRunAsTranslateDlgString(_T("OK"),buf,hDlg,IDOK);
MyRunAsTranslateDlgString(_T("Cancel"),buf,hDlg,IDCANCEL);
HWND h=GetDlgItem(hDlg,IDC_RUNASCURR);
if (GetPrivateProfileInt(_T("MyRunAsCfg"),_T("DisableCurrUserOpt"),false,buf))EnableWindow(h,false);
if (GetPrivateProfileInt(_T("MyRunAsCfg"),_T("HideCurrUserOpt"),false,buf))ShowWindow(h,false);
}
#endif
bool ErrorIsLogonError(DWORD err) {
switch (err) {
case ERROR_LOGON_FAILURE:
case ERROR_ACCOUNT_RESTRICTION:
case ERROR_INVALID_LOGON_HOURS:
case ERROR_INVALID_WORKSTATION:
case ERROR_PASSWORD_EXPIRED:
case ERROR_ACCOUNT_DISABLED:
case ERROR_NONE_MAPPED:
case ERROR_NO_SUCH_USER:
case ERROR_INVALID_ACCOUNT_NAME:
return true;
}
return false;
}
void VerifyOKBtn(HWND hDlg,RUNASDLGDATA*pRADD) {
const bool HasText=pRADD?(pRADD->AsSelf?true:MySndDlgItemMsg(hDlg,IDC_USERNAME,WM_GETTEXTLENGTH)>0):false;
EnableWindow(GetDlgItem(hDlg,IDOK),HasText);
}
void SetDlgState(HWND hDlg,bool AsSelf,RUNASDLGDATA*pRADD) {
if (pRADD)pRADD->AsSelf=AsSelf;
MySndDlgItemMsg(hDlg,IDC_RUNASCURR,BM_SETCHECK,AsSelf?BST_CHECKED:BST_UNCHECKED);
MySndDlgItemMsg(hDlg,IDC_RUNASSPEC,BM_SETCHECK,!AsSelf?BST_CHECKED:BST_UNCHECKED);
int ids[]={IDC_USERNAME,IDC_PASSWORD,IDC_LBLUSER,IDC_LBLPWD};
for (int i=0; i<COUNTOF(ids);++i)EnableWindow(GetDlgItem(hDlg,ids[i]),!AsSelf);
VerifyOKBtn(hDlg,pRADD);
}
INT_PTR CALLBACK MyRunAsDlgProc(HWND hwnd,UINT uMsg,WPARAM wp,LPARAM lp) {
RUNASDLGDATA*pRADD=(RUNASDLGDATA*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
switch(uMsg) {
//case WM_DESTROY:
// break;
case WM_CLOSE:
return DestroyWindow(hwnd);
case WM_INITDIALOG:
{
pRADD=(RUNASDLGDATA*)lp;ASSERT(pRADD);
SetWindowLongPtr(hwnd,GWLP_USERDATA,lp);
Edit_LimitText(GetDlgItem(hwnd,IDC_USERNAME),UNLEN+1+MYMAX_DOMAIN); //room for "foo@BAR" or "BAR\foo"
Edit_LimitText(GetDlgItem(hwnd,IDC_PASSWORD),PWLEN);
const HINSTANCE hSh32=GetModuleHandle(_T("SHELL32.dll"));
const HICON hIco=(HICON)LoadImage(hSh32,MAKEINTRESOURCE(194),IMAGE_ICON,32,32,LR_SHARED);
MySndDlgItemMsg(hwnd,IDC_SHICON,STM_SETICON,(WPARAM)hIco);
SendMessage(hwnd,WM_SETTEXT,0,(LPARAM)g_RunAsDlgTitle);
MySetDlgItemText(hwnd,IDC_HELPTEXT,g_RunAsHelpText);
MyRunAsFmtCurrUserRadio(hwnd,g_RunAsCurrUsrFmt);
MySetDlgItemText(hwnd,IDC_RUNASSPEC,g_RunAsSpecHelp);
#ifdef FEAT_CUSTOMRUNASDLG_TRANSLATE
MyRunAsTranslateDlg(hwnd);
#endif
SetDlgState(hwnd,false,pRADD);
#if defined(BUILD_DBG) && 0 //auto login used during testing ;)
SetDlgItemText(hwnd,IDC_USERNAME,_T("root"));
SetDlgItemText(hwnd,IDC_PASSWORD,_T("???"));
Sleep(1);PostMessage(hwnd,WM_COMMAND,IDOK,0);
#endif
}
return true;
case WM_COMMAND:
{
switch(HIWORD(wp)) {
case EN_CHANGE:
VerifyOKBtn(hwnd,pRADD);
break;
case EN_SETFOCUS:
case BN_CLICKED:
if (LOWORD(wp)<=IDCANCEL)break;
SetDlgState(hwnd,LOWORD(wp)==IDC_RUNASCURR,pRADD);
return FALSE;
}
INT_PTR exitcode=!pRADD?-1:IDCANCEL;
switch(LOWORD(wp)) {
case IDOK:
if (pRADD) {
SHELLEXECUTEINFO&sei=*pRADD->pSEI;
PROCESS_INFORMATION pi={0};
DWORD ec=NO_ERROR;
WCHAR*wszExec;//Also used as TCHAR buffer in AsSelf mode
bool PerformTCharFmt=pRADD->AsSelf;
//const DWORD CommonStartupInfoFlags=STARTF_FORCEONFEEDBACK;
#ifdef UNICODE
PerformTCharFmt=true;
#endif
wszExec=(WCHAR*)NSIS::MemAlloc( (pRADD->AsSelf?sizeof(TCHAR):sizeof(WCHAR)) *(lstrlen(sei.lpFile)+1+lstrlen(sei.lpParameters)+1));
if (!wszExec)ec=ERROR_OUTOFMEMORY;
if (PerformTCharFmt)wsprintf((TCHAR*)wszExec,_T("%s%s%s"),sei.lpFile,((sei.lpParameters&&*sei.lpParameters)?_T(" "):_T("")),sei.lpParameters);
if (!ec) {
if (pRADD->AsSelf) {
STARTUPINFO si={sizeof(si)};
TRACEF("MyRunAs:CreateProcess:%s|\n",wszExec);
ec=(CreateProcess(0,(TCHAR*)wszExec,0,0,false,0,0,0,&si,&pi)?NO_ERROR:GetLastError());
}
else {
//All Wide strings!
WCHAR wszPwd[PWLEN+1];
WCHAR wszUName[UNLEN+1+MYMAX_DOMAIN+1];
STARTUPINFOW siw={sizeof(siw)};
WCHAR*p;
#ifndef UNICODE
//Build unicode string, we already know the buffer is big enough so no error handling
p=wszExec;
MultiByteToWideChar(CP_THREAD_ACP,0,sei.lpFile,-1,p,0xFFFFFF);
if (sei.lpParameters && *sei.lpParameters) {
p+=lstrlen(sei.lpFile);*p++=L' ';*p=0;
MultiByteToWideChar(CP_THREAD_ACP,0,sei.lpParameters,-1,p,0xFFFFFF);
}
#endif
SendMessageW(GetDlgItem(hwnd,IDC_USERNAME),WM_GETTEXT,COUNTOF(wszUName),(LPARAM)wszUName);
SendMessageW(GetDlgItem(hwnd,IDC_PASSWORD),WM_GETTEXT,COUNTOF(wszPwd),(LPARAM)wszPwd);
//Try to find [\\]domain\user and split into username and domain strings
WCHAR*pUName=wszUName,*pDomain=0;
p=wszUName;
//if (*p==p[1]=='\\')pUName=(p+=2);else \ //Should we still split things up if the string starts with \\ ? Is it possible to use \\machine\user at all?
++p;//Don't parse "\something", require at least one char before backslash "?[*\]something"
while(*p && *p!='\\')++p;
if (*p=='\\') {
pDomain=pUName;
pUName=p+1;*p=0;
}
TRACEF("MyRunAs:CreateProcessWithLogonW:%ws|%ws|%ws|%ws|\n",pUName,pDomain?pDomain:L"NO?DOMAIN",wszPwd,wszExec);
ec=(_CreateProcessWithLogonW(pUName,pDomain?pDomain:0,wszPwd,LOGON_WITH_PROFILE,0,wszExec,0,0,0,&siw,&pi)?NO_ERROR:GetLastError());
TRACEF("MyRunAs:CreateProcessWithLogonW: ret=%u\n",ec);
memset(wszPwd, 0, sizeof(wszPwd));
//SecureZeroMemory(wszPwd,sizeof(wszPwd));//if (wszPwd) {volatile WCHAR*_p=wszPwd;for(;_p&&*_p;++_p)*_p=1;if (_p)*wszPwd=0;}//Burn password (And attempt to prevent compiler from removing it)
if (ec && ErrorIsLogonError(ec)) {
LPTSTR szMsg;
DWORD ret=FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,0,ec,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&szMsg,0,0);
if (ret) {
ec=ERRAPP_TRYAGAIN;
MessageBox(hwnd,szMsg,0,MB_ICONWARNING);
LocalFree(szMsg);
}
else ec=GetLastError();
}
}
}
NSIS::MemFree(wszExec);
if (pi.hThread)CloseHandle(pi.hThread);
if (ERRAPP_TRYAGAIN==ec)break;
if (ec) {
SetLastError(ec);
exitcode=-1;
}
else {
pRADD->pSEI->hProcess=pi.hProcess;
exitcode=IDOK;
}
}
case IDCANCEL:
EndDialog(hwnd,exitcode);
}
}
break;
}
return FALSE;
}
DWORD MyRunAs(HINSTANCE hInstDll,SHELLEXECUTEINFO&sei) {
INT_PTR ec;
ASSERT(sei.cbSize>=sizeof(SHELLEXECUTEINFO) && hInstDll);
if (ec=DelayLoadDlls())return ec;
ASSERT(_CreateProcessWithLogonW && _GetUserName);
RUNASDLGDATA radd={0};
radd.pSEI=&sei;
ec=DialogBoxParam(hInstDll,MAKEINTRESOURCE(IDD_MYRUNAS),sei.hwnd,MyRunAsDlgProc,(LPARAM)&radd);
TRACEF("MyRunAs returned %d (%s|%s)\n",ec,sei.lpFile,sei.lpParameters);
switch(ec) {
case 0:
return ERROR_INVALID_HANDLE;//DialogBoxParam returns 0 on bad hwnd
case IDOK:
return NO_ERROR;
case IDCANCEL:
return ERROR_CANCELLED;
}
//TODO:BUGBUG: on vista, the last error seems to get lost, should probably put it in RUNASDLGDATA and always return IDOK
return GetLastError();
}
#ifdef BUILD_DBG
// RunDll exports are __stdcall, we dont care about that for this debug export, rundll32.exe is able to handle this mistake
extern "C" void __declspec(dllexport) __cdecl DBGRDMyRunAs(HWND hwnd,HINSTANCE hinst,LPTSTR lpCmdLine,int nCmdShow) {
SHELLEXECUTEINFO sei={sizeof(sei)};
sei.lpFile=_T("Notepad.exe");//sei.lpParameters=_T("param1");
TRACEF("ec=%d\n",MyRunAs(GetModuleHandle(_T("UAC.dll")),sei));
}
#endif
#endif /* FEAT_CUSTOMRUNASDLG */
//Copyright (C) 2007 Anders Kjersem. Licensed under the zlib/libpng license, see License.txt for details.
/*
UAC plugin for NSIS
===================
Compiled with VC6+PlatformSDK (StdCall & MinimizeSize)
Todo:
-----
GetCurrentDir in elevated parent and pass along to outer process for Exec* (or somekind of ipc to request it if workingdir param is empty)
XCheck if secondary logon service is running in SysElevationPresent() on NT5
Use IsUserAnAdmin? MAKEINTRESOURCE(680) export on 2k (it even exists on NT4?) //http://forums.winamp.com/showthread.php?s=&threadid=195020
AllowSetForegroundWindow
Use RPC instead of WM_COPYDATA for IPC
Autodetect "best" default admin user in MyRunAs
Use ChangeWindowMessageFilter so we can get >WM_USER msg success feedback and possibly fill the log with detailprints
Hide IPC window inside inner instance window? (Could this add unload problems?)
CREATE_PRESERVE_CODE_AUTHZ_LEVEL? http://msdn2.microsoft.com/en-us/library/ms684863.aspx
UpdateProcThreadAttribute?
Consent UI on XP ?
All langs in single file; [MyRunAsStrings]>LangSections != 0 then load strings from [langid] sections
BroadcastSystemMessage to help with SetForeground
UAC::StackPop
Notes:
------
Primary integrity levels:
Name SID RID
Low Mandatory Level S-1-16-4096 0x1000
Medium Mandatory Level S-1-16-8192 0x2000
High Mandatory Level S-1-16-12288 0x3000
System Mandatory Level S-1-16-16384 0x4000
*/
#define UAC_HACK_Clammerz //ugly messagebox focus hack for .onInit
#define UAC_HACK_FGWND1 //super ugly fullscreen invisible window for focus tricks
#define UAC_INITIMPORTS
#include "uac.h"
#include <objbase.h>//CoInitialize
#include "nsisutil.h"
using namespace NSIS;
NSISUTIL_INIT();
#define ERRAPP_BADIPCSRV (0x20000000|1)
#define SW_INVALID ((WORD)-1)
#define IPCTOUT_DEF (1000*3) //default timeout for IPC messages
#define IPCTOUT_SHORT 1500
#define IPCTOUT_LONG (IPCTOUT_DEF*2)
enum _IPCSRVWNDMSG
{
IPC_GETEXITCODE=WM_USER, //Get exit-code of the process spawned by the last call to ExecWait/ShellExecWait
IPC_ELEVATEAGAIN,
IPC_GETSRVPID, //Get PID of outer process
IPC_GETSRVHWND, //Get $HWNDParent of outer process
IPC_EXECCODESEGMENT, //wp:pos | lp:hwnd | returns ErrorCode+1
IPC_GETOUTERPROCESSTOKEN,
#ifdef UAC_HACK_FGWND1
IPC_HACKFINDRUNAS,
#endif
};
enum _COPYDATAID
{
CDI_SHEXEC=666, //returns WindowsErrorCode+1
CDI_SYNCVAR,
CDI_STACKPUSH,
};
typedef struct
{
UINT VarId;
NSISCH buf[ANYSIZE_ARRAY];
} IPC_SYNCVAR;
typedef struct
{
HWND hwnd;
bool Wait;
bool UseCreateProcess;
WORD ShowMode;
NSISCH*strExec;
NSISCH*strParams;
NSISCH*strWorkDir;
NSISCH*strVerb;
NSISCH buf[ANYSIZE_ARRAY];
} IPC_SHEXEC;
typedef struct
{
HINSTANCE hInstance;
HWND hSrvIPC;
BYTE DllRef;
bool UseIPC;
bool CheckedIPCParam;
UINT NSISStrLen;
//IPC Server stuff:
HANDLE hElevatedProcess;
HANDLE threadIPC;
DWORD LastWaitExitCode;//Exit code of process started from last call to ExecWait/ShellExecWait
NSIS::extra_parameters*pXParams;
bool ElevateAgain;
//DelayLoadedModules:
HMODULE hModAdvAPI;
} GLOBALS;
GLOBALS g = {0};
void StopIPCSrv();
DWORD _GetElevationType(TOKEN_ELEVATION_TYPE* pTokenElevType);
bool GetIPCSrvWndFromParams(HWND&hwndOut);
#if _MSC_VER >= 1400 //MSVC 2005 wants to pull in the CRT, let's try to help it out
void* __cdecl memset(void*mem,int c,size_t len)
{
char *p=(char*)mem;
while (len-- > 0){*p++=c;}
return mem;
}
#endif
FORCEINLINE NSISCH* GetIPCWndClass() { return _T("NSISUACIPC"); }
FORCEINLINE bool StrCmpI(LPCTSTR s1,LPCTSTR s2) {return 0==lstrcmpi(s1,s2);}
LPTSTR StrNextChar(LPCTSTR Str) { return CharNext(Str); }
bool StrContainsWhiteSpace(LPCTSTR s) { if (s) {while(*s && *s>_T(' '))s=StrNextChar(s);if (*s)return true;}return false; }
DWORD GetSysVer(bool Major=true)
{
OSVERSIONINFO ovi = { sizeof(ovi) };
if ( !GetVersionEx(&ovi) ) return 0;
return Major ? ovi.dwMajorVersion : ovi.dwMinorVersion;
}
#define GetOSVerMaj() (GetSysVer(true))
#define GetOSVerMin() (GetSysVer(false))
UINT_PTR StrToUInt(LPTSTR s,bool ForceHEX=false,BOOL*pFoundBadChar=0)
{
UINT_PTR v=0;
BYTE base=ForceHEX?16:10;
if (pFoundBadChar)*pFoundBadChar=false;
if ( !ForceHEX && *s=='0' && ((*(s=StrNextChar(s)))&~0x20)=='X' && (s=StrNextChar(s)) )base=16;
for (TCHAR c=*s; c; c=*(s=StrNextChar(s)) )
{
if (c >= _T('0') && c <= _T('9')) c-='0';
else if (base==16 && (c & ~0x20) >= 'A' && (c & ~0x20) <= 'F') c=(c & 7) +9;
else
{
if (pFoundBadChar /*&& c!=' '*/)*pFoundBadChar=true;
break;
}
v*=base;v+=c;
}
return v;
}
LPTSTR FindExePathEnd(LPTSTR p)
{
if ( *p=='"' && *(++p) )
{
while( *p && *p!='"' )p=StrNextChar(p);
if (*p)
p=StrNextChar(p);
else
return 0;
}
else
if ( *p!='/' )while( *p && *p!=' ' )p=StrNextChar(p);
return p;
}
#ifdef FEAT_MSRUNASDLGMODHACK
HHOOK g_MSRunAsHook;
void MSRunAsDlgMod_Unload(void*hook)
{
if (hook)
{
//ASSERT(g_MSRunAsHook==hook);
UnhookWindowsHookEx((HHOOK)hook);
//g_MSRunAsHook=0;
}
}
LRESULT CALLBACK MSRunAsDlgMod_ShellProc(int nCode,WPARAM wp,LPARAM lp)
{
CWPRETSTRUCT*pCWPS;
if (nCode >= 0 && (pCWPS=(CWPRETSTRUCT*)lp) && WM_INITDIALOG==pCWPS->message)
{
TCHAR buf[30];
GetClassName(pCWPS->hwnd,buf,COUNTOF(buf));
if (!lstrcmpi(buf,_T("#32770")))
{
const UINT IDC_USRSAFER=0x106,IDC_OTHERUSER=0x104,IDC_SYSCRED=0x105;
GetClassName(GetDlgItem(pCWPS->hwnd,IDC_SYSCRED),buf,COUNTOF(buf));
if (!lstrcmpi(buf,_T("SysCredential"))) //make sure this is the run as dialog
{
MySndDlgItemMsg(pCWPS->hwnd,IDC_USRSAFER,BM_SETCHECK,BST_UNCHECKED);
MySndDlgItemMsg(pCWPS->hwnd,IDC_OTHERUSER,BM_CLICK);
}
}
}
return CallNextHookEx(g_MSRunAsHook,nCode,wp,lp);
}
void* MSRunAsDlgMod_Init()
{
if(GetOSVerMaj()!=5 || GetOSVerMin()<1)return NULL;//only XP/2003
return g_MSRunAsHook=SetWindowsHookEx(WH_CALLWNDPROCRET,MSRunAsDlgMod_ShellProc,0,GetCurrentThreadId());
}
#endif
DWORD DllSelfAddRef()
{
NSISCH buf[MAX_PATH*5];//Lets hope $pluginsdir is shorter than this, only special builds could break this
DWORD len=GetModuleFileName(g.hInstance,buf,MAX_PATH*5);
if ( len && len<MAX_PATH*5 && LoadLibrary(buf) )
{
if (!g.DllRef)g.DllRef++;
return NO_ERROR;
}
ASSERT(!"DllSelfAddRef failed!");
return ERROR_BUFFER_OVERFLOW;
}
FORCEINLINE DWORD MaintainDllSelfRef() //Call this from every exported function to prevent NSIS from unloading our plugin
{
if(!g.CheckedIPCParam && !g.DllRef)
{
HWND hSrv;
g.CheckedIPCParam=true;
g.UseIPC=GetIPCSrvWndFromParams(hSrv);
if(g.UseIPC)
{
g.DllRef++;
g.hSrvIPC=hSrv;
}
}
return (g.DllRef)?DllSelfAddRef():NO_ERROR;
}
DWORD SendIPCMsg(UINT Msg,WPARAM wp,LPARAM lp,DWORD_PTR&MsgRet,DWORD tout=IPCTOUT_DEF,const HWND hIPCSrv=g.hSrvIPC)
{
if (tout==INFINITE) //BUGFIX: SendMessageTimeout(...,INFINITE,...) seems to be broken, SendMessageTimeout(...,SMTO_NORMAL,0,..) seems to work but why take the chance
{
MsgRet=SendMessage(hIPCSrv,Msg,wp,lp);
return NO_ERROR;
}
if ( SendMessageTimeout(hIPCSrv,Msg,wp,lp,SMTO_NORMAL,tout,&MsgRet) )return NO_ERROR;
return (tout=GetLastError()) ? tout : ERROR_TIMEOUT;
}
void _Unload()
{
StopIPCSrv();
if (g.DllRef)
{
g.DllRef=0;
FreeLibrary(g.hInstance);
//Why bother?> FreeLibrary(g.hModAdvAPI);
}
}
DWORD DelayLoadGetProcAddr(void**ppProc,HMODULE hLib,LPCSTR Export)
{
ASSERT(ppProc && hLib && Export);
if (!*ppProc)
{
*ppProc=(void*)GetProcAddress(hLib,Export);
if (!*ppProc)return GetLastError();
}
return NO_ERROR;
}
DWORD DelayLoadDlls()
{
#ifdef UNICODE
# define __DLD_FUNCSUFFIX "W"
# else
# define __DLD_FUNCSUFFIX "A"
# endif
if (!g.hModAdvAPI) //using g.hModAdvAPI to test if this is the first time we have been called
{
struct
{
HMODULE*pMod;
LPCSTR DllName;//NOTE: Always using ANSI strings to save a couple of bytes
}
dld[]=
{
{&g.hModAdvAPI,"AdvAPI32"},
{0}
};
DWORD ec;
UINT o;
for (o=0; dld[o].pMod; ++o)
if ( !(*dld[o].pMod=LoadLibraryA(dld[o].DllName)) )
return GetLastError();
struct
{
HMODULE hMod;
void**ppProc;
LPCSTR Export;
bool Optional;
}
dldprocs[]=
{
{GetModuleHandle(_T("USER32")),(void**)&_AllowSetForegroundWindow,"AllowSetForegroundWindow",true},
{g.hModAdvAPI,(void**)&_OpenProcessToken, "OpenProcessToken"},
{g.hModAdvAPI,(void**)&_OpenThreadToken, "OpenThreadToken"},
{g.hModAdvAPI,(void**)&_GetTokenInformation, "GetTokenInformation"},
{g.hModAdvAPI,(void**)&_AllocateAndInitializeSid, "AllocateAndInitializeSid"},
{g.hModAdvAPI,(void**)&_FreeSid, "FreeSid"},
{g.hModAdvAPI,(void**)&_EqualSid, "EqualSid"},
{g.hModAdvAPI,(void**)&_CheckTokenMembership, "CheckTokenMembership",true},
#ifdef FEAT_CUSTOMRUNASDLG
{g.hModAdvAPI,(void**)&_GetUserName, "GetUserName" __DLD_FUNCSUFFIX},
{g.hModAdvAPI,(void**)&_CreateProcessWithLogonW,"CreateProcessWithLogonW",true},
{LoadLibraryA("SECUR32"),(void**)&_GetUserNameEx,"GetUserNameEx" __DLD_FUNCSUFFIX,true},//We never free this library
#endif
{0}
};
//#undef __DLD_FUNCSUFFIX
for (o=0; dldprocs[o].hMod; ++o)
if (ec=DelayLoadGetProcAddr(dldprocs[o].ppProc,dldprocs[o].hMod,dldprocs[o].Export) && !dldprocs[o].Optional)
{
TRACEF("DelayLoadDlls failed to find %s in %X\n",dldprocs[o].Export,dldprocs[o].hMod);
return ec;
}
}
return NO_ERROR;
}
void AllowOuterInstanceWindowFocus()
{
if (g.UseIPC)
{
DWORD_PTR MsgRet;
if (!SendIPCMsg(IPC_GETSRVPID,0,0,MsgRet,IPCTOUT_SHORT) && MsgRet && _AllowSetForegroundWindow)_AllowSetForegroundWindow(MsgRet);
}
}
DWORD SyncVars(HWND hwndNSIS)
{
DWORD i,ec=NO_ERROR;
IPC_SYNCVAR*pSV=0;
if (!g.UseIPC)return NO_ERROR;
g.NSISStrLen=NSIS::StrSize;
TRACEF("SyncVars: g.NSISStrLen=%d\n",g.NSISStrLen);ASSERT(g.NSISStrLen>10);
DWORD cbStruct=FIELD_OFFSET(IPC_SYNCVAR,buf[g.NSISStrLen+1]);
pSV=(IPC_SYNCVAR*)MemAlloc(cbStruct);
if (!pSV)
goto die_GLE;
else
{
COPYDATASTRUCT cds={CDI_SYNCVAR,cbStruct,pSV};
for (i=0;i<__INST_LAST && !ec;++i)
{
pSV->VarId=i;
lstrcpyn(pSV->buf,GetVar(i),g.NSISStrLen);
DWORD MsgRet;//TRACEF("SyncVars: (%d)%s|\n",i,pSV->buf);
if (!(ec=SendIPCMsg(WM_COPYDATA,(WPARAM)hwndNSIS,(LPARAM)&cds,MsgRet,3000 )))ec=MsgRet-1;
}
}
return ec;
die_GLE:
return GetLastError();
}
DWORD _Exec(HWND hwnd,NSISCH*Verb,NSISCH*Exec,NSISCH*Params,NSISCH*WorkDir,WORD ShowWnd,bool Wait,bool UseCreateProcess)
{
DWORD ec;
NSISCH*buf=0;
SHELLEXECUTEINFO sei={sizeof(SHELLEXECUTEINFO)};
sei.hwnd =hwnd;
sei.nShow =(ShowWnd!=SW_INVALID)?ShowWnd:SW_NORMAL;
sei.fMask =SEE_MASK_FLAG_DDEWAIT;
sei.lpFile =(Exec&&*Exec) ?Exec:0;
sei.lpParameters=(Params&&*Params) ?Params:0;
sei.lpDirectory =(WorkDir&&*WorkDir) ?WorkDir:0;
sei.lpVerb =(Verb&&*Verb) ?Verb:0;
TRACEF("_Exec:%X|%s|%s|%s|wait=%d useCreateProc=%d ShowWnd=%d useShowWnd=%d\n",hwnd,Exec,Params,WorkDir,Wait,UseCreateProcess,ShowWnd,ShowWnd!=SW_INVALID);
if (UseCreateProcess)
{
STARTUPINFO si={sizeof(STARTUPINFO)};
if (ShowWnd != SW_INVALID)
{
si.dwFlags|=STARTF_USESHOWWINDOW;
si.wShowWindow=sei.nShow;
}
PROCESS_INFORMATION pi;
const NSISCH*Q=( (*Exec!='"') && (*Params) && StrContainsWhiteSpace(Exec)) ? _T("\"") : _T("");//Add extra quotes to program part of command-line?
const DWORD len= ((*Q)?2:0) + lstrlen(Exec) + 1 + lstrlen(Params) + 1;
buf=(NSISCH*)NSIS::MemAlloc(len*sizeof(NSISCH));
if (!buf)return ERROR_OUTOFMEMORY;
//Build string for CreateProcess, "[Q]<Exec>[Q][Space]<Params>"
wsprintf(buf,_T("%s%s%s%s%s"),Q,Exec,Q,((*Params)?_T(" "):_T("")),Params);
TRACEF("_Exec: calling CreateProcess>%s< in >%s< addedQ=%d show=%u\n",buf,sei.lpDirectory,*Q,sei.nShow);
if ( !CreateProcess(0,buf,0,0,false,0,0,sei.lpDirectory,&si,&pi) ) goto die_GLE;
CloseHandle(pi.hThread);
sei.hProcess=pi.hProcess;
}
else
{
sei.fMask|=SEE_MASK_NOCLOSEPROCESS;
TRACEF("_Exec: calling ShellExecuteEx...\n");
if ( !ShellExecuteEx(&sei) )goto die_GLE;
}
if (Wait)
{
WaitForSingleObject(sei.hProcess,INFINITE);
GetExitCodeProcess(sei.hProcess,&g.LastWaitExitCode);
}
else WaitForInputIdle(sei.hProcess,1500);//wait a little bit so the finish page window does not go away too fast and cause focus problems
CloseHandle(sei.hProcess);
ec=NO_ERROR;
ret:
if (buf)NSIS::MemFree(buf);
return ec;
die_GLE:
ec=GetLastError();
TRACEF("_Exec>%s failed with error %u (%s)\n",UseCreateProcess?"CreateProcess":"ShExec",ec,buf);
goto ret;
}
WORD GetShowWndCmdFromStr(NSISCH*s)
{
//NOTE: Little used modes are still supported, just not with strings, you must use the actual number or ${SW_xx} defines from WinMessages.h
struct {NSISCH*id;WORD cmd;} swcm[] = {
{_T("SW_HIDE"), SW_HIDE},
{_T("SW_SHOW"), SW_SHOW},
{_T("SW_RESTORE"), SW_RESTORE},
{_T("SW_MAXIMIZE"), SW_MAXIMIZE},
{_T("SW_MINIMIZE"), SW_MINIMIZE},
// {_T("SW_MAX"), SW_MAXIMIZE},
// {_T("SW_MIN"), SW_MINIMIZE},
{_T("SW_SHOWNORMAL"), SW_SHOWNORMAL},
//{_T("SW_NORMAL"), SW_NORMAL},
//{_T("SW_SHOWMINIMIZED"), SW_SHOWMINIMIZED},
//{_T("SW_SHOWMAXIMIZED"), SW_SHOWMAXIMIZED},
//{_T("SW_SHOWNOACTIVATE"), SW_SHOWNOACTIVATE},
//{_T("SW_SHOWNA"), SW_SHOWNA},
//{_T("SW_SHOWMINNOACTIVE"), SW_SHOWMINNOACTIVE},
//{_T("SW_SHOWDEFAULT"), SW_SHOWDEFAULT},
//{_T("SW_FORCEMINIMIZE"), SW_FORCEMINIMIZE},
{0}
};
for (int i=0; swcm[i].id; ++i) if (StrCmpI(s,swcm[i].id)) return swcm[i].cmd;
return SW_INVALID;
}
#define HasIPCServer() (g.UseIPC!=NULL)
void HandleExecExport(bool CreateProc,bool Wait,HWND&hwndNSIS,int&StrSize,NSISCH*&Vars,stack_t**&StackTop,NSIS::extra_parameters*pXParams)
{
DWORD ec=NO_ERROR,ForkExitCode=ERROR_INVALID_FUNCTION;
UINT cch=0,cbStruct;
WORD ShowWnd;
IPC_SHEXEC*pISE=0;
stack_t* const pSIVerb=CreateProc?0:StackPop();//Only ShellExec supports verb's
stack_t* const pSIShowWnd =StackPop();
stack_t* const pSIExec =StackPop();
stack_t* const pSIParams =StackPop();
stack_t* const pSIWorkDir =StackPop();
if (ec=MaintainDllSelfRef())goto ret;
if (!pSIExec || !pSIParams || !pSIWorkDir || !pSIShowWnd || (!pSIVerb && !CreateProc))
{
TRACE("If you see this you probably forgot that all parameters are required!\n");
ec=ERROR_INVALID_PARAMETER;
goto ret;
}
ShowWnd=GetShowWndCmdFromStr(pSIShowWnd->text);
if (ShowWnd==SW_INVALID && *pSIShowWnd->text)
{
BOOL BadCh;
ShowWnd=StrToUInt(pSIShowWnd->text,false,&BadCh);
if (BadCh)ShowWnd=SW_INVALID;
}
TRACEF("HandleExecExport: ipc=%X (%X)\n",g.UseIPC,g.hSrvIPC);
SyncVars(hwndNSIS);
if (!g.UseIPC) //No IPC Server, we are not elevated with UAC
{
ec=_Exec(hwndNSIS,pSIVerb?pSIVerb->text:0,pSIExec->text,pSIParams->text,pSIWorkDir->text,ShowWnd,Wait,CreateProc);
if (Wait)ForkExitCode=g.LastWaitExitCode;
goto ret;
}
cch+=lstrlen(pSIExec->text)+1;
cch+=lstrlen(pSIParams->text)+1;
cch+=lstrlen(pSIWorkDir->text)+1;
if (pSIVerb)cch+=lstrlen(pSIVerb->text)+1;
cbStruct=FIELD_OFFSET( IPC_SHEXEC, buf[cch*sizeof(TCHAR)] );
pISE=(IPC_SHEXEC*)NSIS::MemAlloc(cbStruct);
if (!pISE)ec=GetLastError();
if (!ec)
{
DWORD_PTR MsgRet;
pISE->hwnd =hwndNSIS;
pISE->Wait =Wait;
pISE->ShowMode =ShowWnd;
pISE->UseCreateProcess=CreateProc;
//Just offsets at this point
pISE->strExec =(NSISCH*)0;
pISE->strParams =(NSISCH*)(lstrlen(pSIExec->text) +pISE->strExec+1);
pISE->strWorkDir=(NSISCH*)(lstrlen(pSIParams->text) +pISE->strParams+1);
pISE->strVerb= (NSISCH*)(lstrlen(pSIWorkDir->text)+pISE->strWorkDir+1);
lstrcpy(pISE->buf,pSIExec->text);
lstrcpy(&pISE->buf[(DWORD_PTR)pISE->strParams], pSIParams->text);
lstrcpy(&pISE->buf[(DWORD_PTR)pISE->strWorkDir],pSIWorkDir->text);
if (pSIVerb)lstrcpy(&pISE->buf[(DWORD_PTR)pISE->strVerb], pSIVerb->text);
COPYDATASTRUCT cds;
cds.dwData=CDI_SHEXEC;
cds.cbData=cbStruct;
cds.lpData=pISE;
AllowOuterInstanceWindowFocus();
if (!(ec=SendIPCMsg(WM_COPYDATA,(WPARAM)hwndNSIS,(LPARAM)&cds,MsgRet,Wait?(INFINITE):(IPCTOUT_LONG) )))ec=MsgRet-1;
TRACEF("HandleExecExport: IPC returned %X, ec=%d\n",MsgRet,ec);
if (Wait && NO_ERROR==ec)
{
ec=SendIPCMsg(IPC_GETEXITCODE,0,0,ForkExitCode);
TRACEF("HandleExecExport(Wait): Spawned process exit code=%d",ForkExitCode);
}
}
ret:
NSIS::MemFree(pISE);
StackFreeItem(pSIShowWnd);
StackFreeItem(pSIExec);
StackFreeItem(pSIParams);
StackFreeItem(pSIWorkDir);
StackFreeItem(pSIVerb);
SetVarUINT(INST_0,ec);
if (ec)SetErrorFlag(pXParams);
if (Wait)SetVarUINT(INST_1,ForkExitCode);
}
bool _SupportsUAC(bool VersionTestOnly=false)
{
TOKEN_ELEVATION_TYPE tet;
OSVERSIONINFO ovi={sizeof(ovi)};
if (!GetVersionEx(&ovi))
{
ASSERT(!"_SupportsUAC>GetVersionEx");
return false;
}
if (VersionTestOnly)return ovi.dwMajorVersion>=6;
if (ovi.dwMajorVersion>=6 && _GetElevationType(&tet)==NO_ERROR)
{
const bool ret=tet!=TokenElevationTypeDefault && tet!=NULL;
TRACEF("_SupportsUAC tet=%d, returning %d\n",tet,ret);
return ret;
}
DBGONLY(TRACEF("_SupportsUAC returning false! ver=%d _GetElevationType.ret=%u\n",ovi.dwMajorVersion,_GetElevationType(&tet)));
return false;
}
DWORD _GetElevationType(TOKEN_ELEVATION_TYPE*pTokenElevType)
{
DWORD ec=ERROR_ACCESS_DENIED;
HANDLE hToken=0;
DWORD RetLen;
if (!pTokenElevType)return ERROR_INVALID_PARAMETER;
if (ec=DelayLoadDlls())return ec;
*pTokenElevType=(TOKEN_ELEVATION_TYPE)NULL;
if (!_SupportsUAC(true))return NO_ERROR;
if (!_OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&hToken))goto dieLastErr;
if (!_GetTokenInformation(hToken,(_TOKEN_INFORMATION_CLASS)TokenElevationType,pTokenElevType,sizeof(TOKEN_ELEVATION_TYPE),&RetLen))goto dieLastErr;
SetLastError(NO_ERROR);
dieLastErr:
ec=GetLastError();
CloseHandle(hToken);
TRACEF("_GetElevationType ec=%u type=%d\n",ec,*pTokenElevType);
return ec;
}
bool _IsUACEnabled()
{
HKEY hKey;
bool ret=false;
if (GetSysVer()>=6 && NO_ERROR==RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),0,KEY_READ,&hKey))
{
//Check must be !=0, see http://codereview.chromium.org/3110 & http://src.chromium.org/viewvc/chrome?view=rev&revision=2330
//Apparently, Vista treats !=0 as UAC on, and some machines have EnableLUA=2 !!
DWORD val,type,size=sizeof(DWORD);
if (NO_ERROR==RegQueryValueEx(hKey,_T("EnableLUA"),0,&type,(LPBYTE)&val,&size) && type==REG_DWORD && val!=0) ret=true;
RegCloseKey(hKey);
}
return ret;
}
bool SysQuery_IsServiceRunning(LPCTSTR servicename)
{
bool retval=false;
SC_HANDLE scdbh=NULL,hsvc;
scdbh=OpenSCManager(NULL,NULL,GENERIC_READ);
if (scdbh)
{
if (hsvc=OpenService(scdbh,servicename,SERVICE_QUERY_STATUS))
{
SERVICE_STATUS ss;
if (QueryServiceStatus(hsvc,&ss))retval=(ss.dwCurrentState==SERVICE_RUNNING);
CloseServiceHandle(hsvc);
}
}
CloseServiceHandle(scdbh);
return retval;
}
inline bool SysNT5IsSecondaryLogonSvcRunning()
{
return SysQuery_IsServiceRunning(_T("seclogon"));
}
bool SysElevationPresent() //Will return false on Vista if UAC is off
{
const DWORD vmaj=GetSysVer();
ASSERT(vmaj<=6 && vmaj>=4);
if (vmaj==5) return true; //TODO:Check if secondary logon service is running?
if (vmaj>=6) return _IsUACEnabled();
return false;
}
FORCEINLINE bool SysSupportsRunAs()
{
return GetSysVer()>=5;
}
bool _IsAdmin()
{
#ifdef BUILD_XPTEST
static int _dbgOld=-1;
unsigned _dbg=(unsigned)FindExePathEnd(GetCommandLine());
if (_dbgOld==-1){_dbg=(_dbg && *((TCHAR*)_dbg))?MessageBoxA(0,"Debug: Pretend to be admin?",GetCommandLine(),MB_YESNOCANCEL):IDCANCEL;} else _dbg=_dbgOld;_dbgOld=_dbg;TRACEF("_IsAdmin=%d|%d\n",_dbg,_dbg==IDYES);
if (_dbg!=IDCANCEL){SetLastError(0);return _dbg==IDYES;}
#endif
BOOL isAdmin=false;
DWORD ec;
OSVERSIONINFO ovi={sizeof(ovi)};
if (!GetVersionEx(&ovi))return false;
if (VER_PLATFORM_WIN32_NT != ovi.dwPlatformId) //Not NT
{
SetLastError(NO_ERROR);
return true;
}
if (ec=DelayLoadDlls())
{
TRACEF("DelayLoadDlls failed in _IsAdmin() with err x%X\n",ec);
SetLastError(ec);
return false;
}
ASSERT(_OpenThreadToken && _OpenProcessToken && _AllocateAndInitializeSid && _EqualSid && _FreeSid);
HANDLE hToken;
if (_OpenThreadToken(GetCurrentThread(),TOKEN_QUERY,FALSE,&hToken) || _OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&hToken))
{
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
PSID psid=0;
if (_AllocateAndInitializeSid(&SystemSidAuthority,2,SECURITY_BUILTIN_DOMAIN_RID,DOMAIN_ALIAS_RID_ADMINS,0,0,0,0,0,0,&psid))
{
if (_CheckTokenMembership)
{
if (!_CheckTokenMembership(0,psid,&isAdmin))isAdmin=false;
}
else
{
DWORD cbTokenGrps;
if (!_GetTokenInformation(hToken,TokenGroups,0,0,&cbTokenGrps)&&GetLastError()==ERROR_INSUFFICIENT_BUFFER)
{
TOKEN_GROUPS*ptg=0;
if (ptg=(TOKEN_GROUPS*)NSIS::MemAlloc(cbTokenGrps))
{
if (_GetTokenInformation(hToken,TokenGroups,ptg,cbTokenGrps,&cbTokenGrps))
{
for (UINT i=0; i<ptg->GroupCount;i++)
{
if (_EqualSid(ptg->Groups[i].Sid,psid))isAdmin=true;
}
}
NSIS::MemFree(ptg);
}
}
}
_FreeSid(psid);
}
CloseHandle(hToken);
}
if (isAdmin) //UAC Admin with split token check
{
if (_SupportsUAC())
{
TOKEN_ELEVATION_TYPE tet;
if (_GetElevationType(&tet) || tet==TokenElevationTypeLimited)isAdmin=false;
}
else SetLastError(NO_ERROR);
}
return FALSE != isAdmin;
}
LRESULT CALLBACK IPCSrvWndProc(HWND hwnd,UINT uMsg,WPARAM wp,LPARAM lp)
{
switch(uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CLOSE:
return DestroyWindow(hwnd);
case WM_COPYDATA:
if (lp)
{
const COPYDATASTRUCT*pCDS=(COPYDATASTRUCT*)lp;
if (pCDS->dwData==CDI_SHEXEC && pCDS->lpData)
{
if ( pCDS->cbData < sizeof(IPC_SHEXEC) )break;
g.LastWaitExitCode=ERROR_INVALID_FUNCTION;
IPC_SHEXEC& ise=*((IPC_SHEXEC*)pCDS->lpData);
SetForegroundWindow(ise.hwnd);
DWORD ec=_Exec(
ise.hwnd,
&ise.buf[(DWORD_PTR)ise.strVerb],
&ise.buf[(DWORD_PTR)ise.strExec],
&ise.buf[(DWORD_PTR)ise.strParams],
&ise.buf[(DWORD_PTR)ise.strWorkDir],
ise.ShowMode,ise.Wait,ise.UseCreateProcess
);
TRACEF("IPCSrvWndProc>IPC_SHEXEC>_ShExec=%d\n",ec);
return ec+1;
}
else if (pCDS->dwData==CDI_SYNCVAR && pCDS->lpData && pCDS->cbData>1)
{
IPC_SYNCVAR*pSV=(IPC_SYNCVAR*)pCDS->lpData;
if (pSV->VarId>=__INST_LAST)return 1+ERROR_INVALID_PARAMETER;
TRACEF("WM_COPYDATA: CDI_SYNCVAR:%d=%s|\n",pSV->VarId,&pSV->buf[0]);
lstrcpy(GetVar(pSV->VarId),&pSV->buf[0]);
return NO_ERROR+1;
}
else if (pCDS->dwData==CDI_STACKPUSH && pCDS->lpData && pCDS->cbData>=1)
{
TRACEF("WM_COPYDATA: CDI_STACKPUSH:%s|\n",pCDS->lpData);
return StackPush((NSISCH*)pCDS->lpData)+1;
}
}
break;
case IPC_GETEXITCODE:
return g.LastWaitExitCode;
case IPC_ELEVATEAGAIN:
TRACE("IPCSrvWndProc>IPC_ELEVATEAGAIN\n");
return (g.ElevateAgain=true);
case IPC_GETSRVPID:
return GetCurrentProcessId();
case IPC_GETSRVHWND:
return GetWindowLongPtr(hwnd,GWLP_USERDATA);
case IPC_EXECCODESEGMENT:
return 1+(g.pXParams ? ExecuteCodeSegment(g.pXParams,wp,(HWND)lp) : ERROR_INVALID_FUNCTION);
case IPC_GETOUTERPROCESSTOKEN:
if (_OpenProcessToken)
{
HANDLE hToken,hOutToken;
if (_OpenProcessToken(GetCurrentProcess(),TOKEN_ALL_ACCESS,&hToken))
{
TRACEF("IPC_GETOUTERPROCESSTOKEN: hToken=%X targetProcess=%X\n",hToken,g.hElevatedProcess);
if (DuplicateHandle(GetCurrentProcess(),hToken,g.hElevatedProcess,&hOutToken,lp,false,DUPLICATE_CLOSE_SOURCE))
{
TRACEF("IPC_GETOUTERPROCESSTOKEN: %X(%X) > %X(%X)\n",hToken,-1,hOutToken,g.hElevatedProcess);
return (LRESULT)hOutToken;
}
}
}
return NULL;
#ifdef UAC_HACK_FGWND1
case IPC_HACKFINDRUNAS: //super ugly hack to get title of run as dialog
if (wp<200)
{
HWND hRA=GetLastActivePopup((HWND)lp);
TRACEF("IPC_HACKFINDRUNAS:%d %X %X\n",wp,lp,hRA);
if (hRA && hRA !=(HWND)lp ) return PostMessage((HWND)lp,WM_APP,0,(LONG_PTR)hRA);
Sleep(10);PostMessage(hwnd,uMsg,wp+1,lp);
}
break;
#endif
}
return DefWindowProc(hwnd,uMsg,wp,lp);
}
DWORD WINAPI IPCSrvThread(LPVOID lpParameter)
{
CoInitialize(0);
const DWORD WStyle=WS_VISIBLE DBGONLY(|(WS_CAPTION));
const int PosOffset=32700;
MSG msg;
WNDCLASS wc={0};
wc.lpszClassName=GetIPCWndClass();
wc.lpfnWndProc=IPCSrvWndProc;
wc.hInstance=g.hInstance;
if (!RegisterClass(&wc))goto dieLastErr;
if (!(g.hSrvIPC=CreateWindowEx(WS_EX_TOOLWINDOW DBGONLY(&~WS_EX_TOOLWINDOW),
GetIPCWndClass(),
DBGONLY(_T("Debug: NSIS.UAC")+)0,
WStyle,
-PosOffset DBGONLY(+PosOffset),-PosOffset DBGONLY(+PosOffset),DBGONLY(150+)1,DBGONLY(10+)1,
0,0,wc.hInstance,0
)))goto dieLastErr;
SetWindowLongPtr(g.hSrvIPC,GWLP_USERDATA,(LONG_PTR)lpParameter);
TRACEF("IPCSrv=%X server created...\n",g.hSrvIPC);
while (GetMessage(&msg,0,0,0)>0)DispatchMessage(&msg);
SetLastError(NO_ERROR);
dieLastErr:
CoUninitialize();
return g.LastWaitExitCode=GetLastError();
}
DWORD InitIPC(HWND hwndNSIS,NSIS::extra_parameters*pXParams,UINT NSISStrLen)
{
if (g.threadIPC)return NO_ERROR;
TRACEF("InitIPC StrSize=%u vs %u\n",NSIS::StrSize,NSISStrLen);
DWORD tid;
ASSERT(!g.pXParams && pXParams);
ASSERT(NSISStrLen>0 && NSISStrLen==NSIS::StrSize);
g.NSISStrLen=NSISStrLen;
g.pXParams=pXParams;
g.threadIPC=CreateThread(0,0,IPCSrvThread,hwndNSIS,0,&tid);
if (g.threadIPC)
{
while(!g.hSrvIPC && !g.LastWaitExitCode)Sleep(20);
return g.hSrvIPC ? NO_ERROR : g.LastWaitExitCode;
}
return GetLastError();
}
void StopIPCSrv()
{
if (g.threadIPC)
{
TRACEF("StopIPCSrv h=%X \n",g.hSrvIPC);
#ifdef UAC_HACK_Clammerz
if ( GetSysVer()>=5 )
{
//WINBUGFIX: This ugly thing supposedly solves the problem of messagebox'es in .OnInit appearing behind other windows in Vista
HWND hack=CreateWindowEx(WS_EX_TRANSPARENT|WS_EX_LAYERED,_T("Button"),NULL,NULL,0,0,0,0,NULL,NULL,NULL,0);
ShowWindow(hack,SW_SHOW);
SetForegroundWindow(hack);
DestroyWindow(hack);
}
#endif
PostMessage(g.hSrvIPC,WM_CLOSE,0,0);
WaitForSingleObject(g.threadIPC,INFINITE);
CloseHandle(g.threadIPC);
UnregisterClass(GetIPCWndClass(),g.hInstance);//DLL can be loaded more than once, so make sure RegisterClass doesn't fail
g.hSrvIPC=0;
g.threadIPC=0;
}
}
#ifdef UAC_HACK_FGWND1
LRESULT CALLBACK HackWndSubProc(HWND hwnd,UINT Msg,WPARAM wp,LPARAM lp)
{
switch(Msg)
{
case WM_APP:
GetWindowText((HWND)lp,GetVar(0),NSIS::StrSize);
if (*GetVar(0))SendMessage(hwnd,WM_SETTEXT,0,(LONG_PTR)GetVar(0));
break;
}
return DefWindowProc(hwnd,Msg,wp,lp);
}
#endif
inline bool MustUseInternalRunAs()
{
#ifdef BUILD_DBGSELECTELVMODE
TCHAR dbgb[MAX_PATH*4];wsprintf(dbgb,_T("%s.ini"),GetVar(VIDX_EXEPATH));
static int dbg_answer=GetPrivateProfileInt(_T("UACDBG"),_T("MustUseInternalRunAs"),2,dbgb);
if (dbg_answer<2)return !!dbg_answer;WritePrivateProfileString(_T("UACDBG"),_T("MustUseInternalRunAs"),"",dbgb);
if (MessageBox(GetActiveWindow(),"MustUseInternalRunAs?",dbgb,MB_YESNO)==IDYES)return true;
#endif
return GetSysVer()>=6 && !SysElevationPresent();
}
DWORD ForkSelf(HWND hParent,DWORD&ForkExitCode,NSIS::extra_parameters*pXParams,UINT NSISStrLen)
{
DWORD ec=ERROR_ACCESS_DENIED;
SHELLEXECUTEINFO sei={sizeof(sei)};
//STARTUPINFO startInfo={sizeof(STARTUPINFO)};
LPTSTR pszExePathBuf=0;
LPTSTR pszParamBuf=0;
LPTSTR p,pCL=GetCommandLine();
UINT len;
const DWORD OSVerMaj=GetOSVerMaj();
#ifdef UAC_HACK_FGWND1
HWND hHack=0;
#endif
ASSERT(pXParams);
//GetStartupInfo(&startInfo);
if (ec=InitIPC(hParent,pXParams,NSISStrLen))goto ret;
ASSERT(IsWindow(g.hSrvIPC));
sei.hwnd=hParent;
sei.nShow=/*(startInfo.dwFlags&STARTF_USESHOWWINDOW) ? startInfo.wShowWindow :*/ SW_SHOWNORMAL;
sei.fMask=SEE_MASK_NOCLOSEPROCESS|SEE_MASK_NOZONECHECKS;
sei.lpVerb=_T("runas");
p=FindExePathEnd(pCL);
len=p-pCL;
if (!p || !len)
{
ec=ERROR_FILE_NOT_FOUND;
goto ret;
}
for (;;)
{
NSIS::MemFree(pszExePathBuf);
if (!(pszExePathBuf=(LPTSTR)NSIS::MemAlloc((++len)*sizeof(TCHAR))))goto dieOOM;
if ( GetModuleFileName(0,pszExePathBuf,len) < len )break; //FUCKO: what if GetModuleFileName returns 0?
len+=MAX_PATH;
}
sei.lpFile=pszExePathBuf;
len=lstrlen(p);
len+=20;//space for "/UAC:xxxxxx /NCRC\0"
if (!(pszParamBuf=(LPTSTR)NSIS::MemAlloc(len*sizeof(TCHAR))))goto dieOOM;
wsprintf(pszParamBuf,_T("/UAC:%X /NCRC%s"),g.hSrvIPC,p);//NOTE: The argument parser depends on our special flag appearing first
sei.lpParameters=pszParamBuf;
if (OSVerMaj==5)
{
bool hasseclogon=SysNT5IsSecondaryLogonSvcRunning();
TRACEF("SysNT5IsSecondaryLogonSvcRunning=%d\n",hasseclogon);
if (!hasseclogon)
{
ec=ERROR_SERVICE_NOT_ACTIVE;
goto ret;
}
}
#ifdef UAC_HACK_FGWND1
if ( OSVerMaj>=5 && !sei.hwnd )
{
//sei.nShow=SW_SHOW;//forced, do we HAVE to do this?
hHack=CreateWindowEx(WS_EX_TRANSPARENT|WS_EX_LAYERED|WS_EX_TOOLWINDOW|WS_EX_APPWINDOW,_T("Button"),GetVar(VIDX_EXEFILENAME),0|WS_MAXIMIZE,0,0,0,0,NULL,NULL,NULL,0);
SetWindowLongPtr(hHack,GWLP_WNDPROC,(LONG_PTR)HackWndSubProc);
if (GetSysVer()<6 || MustUseInternalRunAs())
PostMessage(g.hSrvIPC,IPC_HACKFINDRUNAS,0,(LONG_PTR)hHack);
else
SetWindowLongPtr(hHack,GWL_EXSTYLE,GetWindowLongPtr(hHack,GWL_EXSTYLE)&~WS_EX_APPWINDOW);//kill taskbar btn on vista
HICON hIcon=(HICON)LoadImage(GetModuleHandle(0),MAKEINTRESOURCE(103),IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_SHARED);
SendMessage(hHack,WM_SETICON,ICON_BIG,(LONG_PTR)hIcon);
ShowWindow(hHack,SW_SHOW);
SetForegroundWindow(hHack);
sei.hwnd=hHack;
}
#endif
if (hParent)SetForegroundWindow(hParent);//try to force taskbar button active (for RunElevatedAndProcessMessages, really important on Vista)
#ifdef FEAT_CUSTOMRUNASDLG
if (MustUseInternalRunAs())
{
ec=MyRunAs(g.hInstance,sei);
}
else
#endif
{
if (GetSysVer()>=6)
{
////////sei.nShow=SW_SHOW;
//if ( _SupportsUAC() )sei.hwnd=0; //Vista does not like it when we provide a HWND
//if (_AllowSetForegroundWindow) _AllowSetForegroundWindow(ASFW_ANY);//TODO: is GrantClientWindowInput() enough?
}
#ifdef FEAT_MSRUNASDLGMODHACK
void* hook=MSRunAsDlgMod_Init();
#endif
TRACEF("ForkSelf:calling ShExec:app=%s|params=%s|vrb=%s|hwnd=%X\n",sei.lpFile,sei.lpParameters,sei.lpVerb,sei.hwnd);
ec=(ShellExecuteEx(&sei)?NO_ERROR:GetLastError());
TRACEF("ForkSelf: ShExec->Runas returned %d hInstApp=%d\n",ec,sei.hInstApp);
#ifdef FEAT_MSRUNASDLGMODHACK
MSRunAsDlgMod_Unload(hook);
#endif
}
#ifdef UAC_HACK_FGWND1
DestroyWindow(hHack);
#endif
if (ec)goto ret;
TRACEF("ForkSelf: waiting for process %X (%s|%s|%s)sei.hwnd=%X\n",(sei.hProcess),sei.lpFile,sei.lpParameters,sei.lpVerb,sei.hwnd);
ASSERT(sei.hProcess);
ASSERT(NO_ERROR==ec);
ShowWindow(g.hSrvIPC,SW_HIDE);
g.hElevatedProcess=sei.hProcess;
if (!IsWindow(sei.hwnd))
{
DWORD w=WaitForSingleObject(sei.hProcess,INFINITE);
if (w==WAIT_OBJECT_0)
VERIFY(GetExitCodeProcess(sei.hProcess,&ForkExitCode));
else
{
ec=GetLastError();
TRACEF("ForkSelf:WaitForSingleObject failed ec=%d w=%d\n",ec,w);ASSERT(!"ForkSelf:WaitForSingleObject");
}
}
else
{
bool abortWait=false;
const DWORD waitCount=1;
const HANDLE handles[waitCount]={sei.hProcess};
do
{
DWORD w=MsgWaitForMultipleObjects(waitCount,handles,false,INFINITE,QS_ALLEVENTS|QS_ALLINPUT);
switch(w)
{
case WAIT_OBJECT_0:
VERIFY(GetExitCodeProcess(sei.hProcess,&ForkExitCode));
abortWait=true;
break;
case WAIT_OBJECT_0+waitCount:
{
const HWND hwnd=sei.hwnd;
MSG msg;
while( !ec && PeekMessage(&msg,hwnd,0,0,PM_REMOVE) )
{
if (msg.message==WM_QUIT)
{
ASSERT(0);
ec=ERROR_CANCELLED;
break;
}
if (!IsDialogMessage(hwnd,&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
break;
default:
abortWait=true;
ec=GetLastError();
TRACEF("ForkSelf:MsgWaitForMultipleObjects failed, ec=%u w=%u\n",ec,w);
}
} while( NO_ERROR==ec && !abortWait );
}
TRACEF("ForkSelf: wait complete, ec=%d forkexitcode=%u\n",ec,ForkExitCode);
goto ret;
dieOOM:
ec=ERROR_OUTOFMEMORY;
ret:
StopIPCSrv();
CloseHandle(sei.hProcess);
NSIS::MemFree(pszExePathBuf);
NSIS::MemFree(pszParamBuf);
return ec;
}
bool GetIPCSrvWndFromParams(HWND&hwndOut)
{
LPTSTR p=FindExePathEnd(GetCommandLine());
while(p && *p==' ')p=CharNext(p);TRACEF("GetIPCSrvWndFromParams:%s|\n",p);
if (p && *p++=='/'&&*p++=='U'&&*p++=='A'&&*p++=='C'&&*p++==':')
{
hwndOut=(HWND)StrToUInt(p,true);
return !!IsWindow(hwndOut);
}
return false;
}
/*** RunElevated
Return: r0: Windows error code (0 on success, 1223 if user aborted elevation dialog, anything else should be treated as a fatal error)
r1: If r0==0, r1 is:
0 if UAC is not supported by the OS,
1 if UAC was used to elevate and the current process should act like a wrapper (Call Quit in .OnInit without any further processing),
2 if the process is (already?) running @ HighIL (Member of admin group on other systems),
3 if you should call RunElevated again (This can happen if a user without admin priv. is used in the runas dialog),
r2: If r0==0 && r1==1: r2 is the ExitCode of the elevated fork process (The NSIS errlvl is also set to the ExitCode)
r3: If r0==0: r3 is 1 if the user is a member of the admin group or 0 otherwise
*/
EXPORTNSISFUNC RunElevated(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
BYTE UACMode=0;
bool UserIsAdmin=false;
DWORD ec=ERROR_ACCESS_DENIED,ForkExitCode;
TOKEN_ELEVATION_TYPE tet;
UserIsAdmin=_IsAdmin();
TRACEF("RunElevated:Init: IsAdmin=%d\n",UserIsAdmin);
#ifdef BUILD_XPTEST
if (!UserIsAdmin)goto DbgXPAsUAC;//Skip UAC detection for special debug builds and force a call to runas on <NT6 systems
#endif
if (!_SupportsUAC() && !SysSupportsRunAs())goto noUAC;
if ((ec=DelayLoadDlls()))goto ret;
if (GetIPCSrvWndFromParams(g.hSrvIPC))
{
if (ec=DllSelfAddRef())goto ret;
if (!IsWindow(g.hSrvIPC))ec=ERRAPP_BADIPCSRV;
UACMode=2;
g.UseIPC=true;
if (!UserIsAdmin) //Elevation used, but we are not Admin, let the wrapper know so it can try again...
{
UACMode=0xFF;//Special invalid mode
DWORD_PTR MsgRet;
if (SendIPCMsg(IPC_ELEVATEAGAIN,0,0,MsgRet) || !MsgRet)ec=ERRAPP_BADIPCSRV;//if we could not notify the need for re-elevation, the IPCSrv must be bad
}
goto ret;
}
if ( (ec=DllSelfAddRef()) || (ec=_GetElevationType(&tet)) )goto ret;
if ( tet==TokenElevationTypeFull || UserIsAdmin )
{
UserIsAdmin=true;
UACMode=2;
goto ret;
}
DBGONLY(DBG_RESETDBGVIEW());
#ifdef BUILD_XPTEST
DbgXPAsUAC:VERIFY(!DllSelfAddRef());
#endif
//OS supports UAC and we need to elevate...
ASSERT(!UserIsAdmin);
UACMode=1;
ec=ForkSelf(hwndNSIS,ForkExitCode,XParams,StrSize);
if (!ec && !g.ElevateAgain)
{
SetVarUINT(INST_2,ForkExitCode);
SetErrLvl(XParams,ForkExitCode);
}
goto ret;
noUAC:
ec=NO_ERROR;ASSERT(UACMode==0);
ret:
if (ec==ERROR_CANCELLED)
{
if (UACMode!=1)ec=ERROR_INVALID_FUNCTION;
if (UACMode<2)g.UseIPC=false;
}
if (UACMode==0xFF && !ec) //We called IPC_ELEVATEAGAIN, so we need to quit so the wrapper gains control
{
ASSERT(g.UseIPC);
UACMode=1;//We pretend to be the wrapper so Quit gets called in .OnInit
SetErrLvl(XParams,0);
_Unload();
}
if (g.ElevateAgain)
{
ASSERT(!g.UseIPC);
UACMode=3;//Fork called IPC_ELEVATEAGAIN, we need to change our UACMode so the wrapper(our instance) can try to elevate again if it wants to
}
if (!g.UseIPC)
_Unload();//The wrapper can call quit in .OnInit without calling UAC::Unload, so we do it here
SetVarUINT(INST_0,ec);
SetVarUINT(INST_1,UACMode);
SetVarUINT(INST_3,UserIsAdmin);
TRACEF("RunElevated returning ec=%X UACMode=%d g.UseIPC=%d g.ElevateAgain=%d IsAdmin=%d\n",ec,UACMode,g.UseIPC,g.ElevateAgain,UserIsAdmin);
NSISFUNCEND();
}
/*** Exec
Notes:
ErrorFlag is also set on error
STACK: <ShowWindow> <File> <Parameters> <WorkingDir>
Return: windows error code in r0, 0 on success
*/
EXPORTNSISFUNC Exec(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
HandleExecExport(true,false,hwndNSIS,StrSize,Vars,StackTop,XParams);
NSISFUNCEND();
}
/*** ExecWait
Notes:
ErrorFlag is also set on error
STACK: <ShowWindow> <File> <Parameters> <WorkingDir>
Return:
r0: windows error code, 0 on success
r1: exitcode of new process
*/
EXPORTNSISFUNC ExecWait(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
HandleExecExport(true,true,hwndNSIS,StrSize,Vars,StackTop,XParams);
NSISFUNCEND();
}
/*** ShellExec
Notes:
ErrorFlag is also set on error
STACK: <Verb> <ShowWindow> <File> <Parameters> <WorkingDir>
Return: windows error code in r0, 0 on success
*/
EXPORTNSISFUNC ShellExec(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
HandleExecExport(false,false,hwndNSIS,StrSize,Vars,StackTop,XParams);
NSISFUNCEND();
}
/*** ShellExecWait
Notes:
ErrorFlag is also set on error
STACK: <Verb> <ShowWindow> <File> <Parameters> <WorkingDir>
Return:
r0: windows error code, 0 on success
r1: exitcode of new process
*/
EXPORTNSISFUNC ShellExecWait(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
HandleExecExport(false,true,hwndNSIS,StrSize,Vars,StackTop,XParams);
NSISFUNCEND();
}
/*** GetElevationType
Notes:
TokenElevationTypeDefault=1 :User is not using a split token (UAC disabled)
TokenElevationTypeFull=2 :UAC enabled, the process is elevated
TokenElevationTypeLimited=3 :UAC enabled, the process is not elevated
Return: r0: (TOKEN_ELEVATION_TYPE)TokenType, The error flag is set if the function fails and r0==0
*/
EXPORTNSISFUNC GetElevationType(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
TOKEN_ELEVATION_TYPE tet=(TOKEN_ELEVATION_TYPE)NULL; //Default to invalid value
if (MaintainDllSelfRef() || /*!_SupportsUAC() ||*/ _GetElevationType(&tet)) SetErrorFlag(XParams);
SetVarUINT(INST_0,tet);
NSISFUNCEND();
}
/*** SupportsUAC
Notes: Check if the OS supports UAC (And the user has UAC turned on)
Return: r0: (BOOL)Result
*/
EXPORTNSISFUNC SupportsUAC(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
BOOL present=false;
MaintainDllSelfRef();
if (_SupportsUAC())present=true;
SetVarUINT(INST_0,present);
NSISFUNCEND();
}
/*** IsAdmin
Return: r0: (BOOL)Result
*/
EXPORTNSISFUNC IsAdmin(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
bool Admin=false;
DWORD ec;
if ( !(ec=MaintainDllSelfRef()) )
{
Admin=_IsAdmin();
if ( ec=GetLastError() )
{
TRACEF("IsAdmin failed with %d\n",ec);
SetErrorFlag(XParams);
Admin=false;
}
}
SetVarUINT(INST_0,Admin);
NSISFUNCEND();
}
/*** ExecCodeSegment
Notes: Sets error flag on error
There is currently no way to transfer state to/from the executed code segment!
If you use instructions that alter the UI or the stack/variables in the code segment (StrCpy,Push/Pop/Exch,DetailPrint,HideWindow etc.) they will affect the hidden wrapper installer and not "your" installer instance!
*/
EXPORTNSISFUNC ExecCodeSegment(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
DWORD ec;
if (!(ec=DllSelfAddRef())) //force AddRef since our plugin could be called inside the executed code segment!
{
ec=ERROR_INVALID_PARAMETER;
stack_t* pSI=StackPop();
if (pSI)
{
BOOL badCh;
UINT pos=StrToUInt(pSI->text,false,&badCh);
TRACEF("ExecCodeSegment %d (%s) badinput=%d\n",pos-1,pSI->text,badCh);
if (!badCh && pos--)
{
if (!g.UseIPC)
ec=NSIS::ExecuteCodeSegment(XParams,pos);
else
{
SyncVars(hwndNSIS);
DWORD_PTR MsgRet;
AllowOuterInstanceWindowFocus();
if (!(ec=SendIPCMsg(IPC_EXECCODESEGMENT,pos,0,MsgRet,INFINITE)))ec=MsgRet-1;
}
}
StackFreeItem(pSI);
}
}
if (ec)SetErrorFlag(XParams);
NSISFUNCEND();
}
/*** StackPush */
EXPORTNSISFUNC StackPush(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
DWORD ec;
if (!(ec=DllSelfAddRef()) && g.UseIPC)
{
stack_t* pSI=StackPop();
ec=ERROR_INVALID_PARAMETER;
if (pSI)
{
DWORD_PTR MsgRet;
COPYDATASTRUCT cds={CDI_STACKPUSH,(lstrlen(pSI->text)+1)*sizeof(NSISCH),pSI->text};
if (!(ec=SendIPCMsg(WM_COPYDATA,(WPARAM)hwndNSIS,(LPARAM)&cds,MsgRet,5000 )))ec=MsgRet-1;
StackFreeItem(pSI);
}
}
if (ec)SetErrorFlag(XParams);
NSISFUNCEND();
}
/*** GetOuterHwnd */
EXPORTNSISFUNC GetOuterHwnd(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
MaintainDllSelfRef();
DWORD_PTR MsgRet;HWND hSrvIPC;
if (!GetIPCSrvWndFromParams(hSrvIPC)||!IsWindow(hSrvIPC)||SendIPCMsg(IPC_GETSRVHWND,0,0,MsgRet,IPCTOUT_DEF,hSrvIPC))MsgRet=0;
SetVarUINT(INST_0,MsgRet);
NSISFUNCEND();
}
DWORD SetPrivilege(LPCSTR PrivName,bool Enable)
{
DWORD r=NO_ERROR;
HANDLE hToken=NULL;
TOKEN_PRIVILEGES TokenPrivs;
if (!LookupPrivilegeValueA(NULL,PrivName,&TokenPrivs.Privileges[0].Luid))goto dieGLE;
if (!_OpenProcessToken(GetCurrentProcess (),TOKEN_ADJUST_PRIVILEGES,&hToken))goto dieGLE;
TokenPrivs.Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0 ;
TokenPrivs.PrivilegeCount=1;
if (AdjustTokenPrivileges(hToken,FALSE,&TokenPrivs,0,NULL,NULL))goto ret;
dieGLE:
r=GetLastError();
ret:
CloseHandle(hToken);
return r;
}
#include <shlobj.h>
/*** GetShellFolderPath */
EXPORTNSISFUNC GetShellFolderPath(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
/*
WINBUG:
For some reason, even with debug priv. enabled, a call to OpenProcessToken(TOKEN_READ|TOKEN_IMPERSONATE)
will fail, even if we have a PROCESS_ALL_ACCESS handle on XP when running as a member of the Power Users Group.
So we have to ask the outer process to give us the handle.
*/
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
MaintainDllSelfRef();
const unsigned TokenAccessRights=TOKEN_READ|TOKEN_IMPERSONATE;
unsigned clsidFolder;
HWND hSrvIPC;
HANDLE hToken=NULL,hOuterProcess=NULL;
DWORD SrvPID;
HRESULT hr;
FARPROC pfnSHGetFolderPath;
LPTSTR buf=GetVar(INST_0);
stack_t*pssCLSID=StackPop();
stack_t*pssFallback=NULL;
BOOL ConvBadCh;
if (!pssCLSID || !(pssFallback=StackPop())) goto fail;
clsidFolder=StrToUInt(pssCLSID->text,false,&ConvBadCh);
if (ConvBadCh)goto fail;
TRACEF("GetShellFolderPath HasIPCServer=%X param=%s>%X fallback=%s|\n",HasIPCServer(),pssCLSID->text,clsidFolder,pssFallback->text);
pfnSHGetFolderPath=GetProcAddress(GetModuleHandle(_T("SHELL32")),"SHGetFolderPath"__DLD_FUNCSUFFIX);
if (!pfnSHGetFolderPath)goto fail;
if (GetIPCSrvWndFromParams(hSrvIPC) && 0 != GetWindowThreadProcessId(hSrvIPC,&SrvPID))
{
hOuterProcess=OpenProcess(PROCESS_QUERY_INFORMATION,false,SrvPID);
if (hOuterProcess)
{
BOOL bOk=OpenProcessToken(hOuterProcess,TokenAccessRights,&hToken);
CloseHandle(hOuterProcess);
if (bOk)goto gotToken;
}
/* SetPrivilege(SE_DEBUG_NAME,true);
hOuterProcess=OpenProcess(PROCESS_DUP_HANDLE,false,SrvPID);
SetPrivilege(SE_DEBUG_NAME,false);
if (!hOuterProcess)goto fail;
TRACEF("hOuterProcess=%X\n",hOuterProcess);
*/ SendIPCMsg(IPC_GETOUTERPROCESSTOKEN,0,TokenAccessRights,(DWORD_PTR&)hToken,IPCTOUT_DEF,hSrvIPC);
TRACEF("IPC_GETOUTERPROCESSTOKEN=%X\n",hToken);//*/
}
gotToken:
if (HasIPCServer() && !hToken)goto fail;
hr=((HRESULT(WINAPI*)(HWND,int,HANDLE,DWORD,LPTSTR))pfnSHGetFolderPath)(hwndNSIS,clsidFolder,hToken,SHGFP_TYPE_CURRENT,buf);
TRACEF("SHGetFolderPath hr=%X with token=%X, clsidFolder=%X|%s\n",hr,hToken,clsidFolder,buf);
if (FAILED(hr)) goto fail; else goto ret;
fail:
TRACEF("GetShellFolderPath GLE=%X\n",GetLastError());
lstrcpy(buf,pssFallback->text);
TRACEF("%s|%s\n",buf,pssFallback->text);
ret:
// CloseHandle(hOuterProcess);
CloseHandle(hToken);
StackFreeItem(pssFallback);
StackFreeItem(pssCLSID);
NSISFUNCEND();
}
/*** GetOuterPID * /
EXPORTNSISFUNC GetOuterPID(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop,NSIS::extra_parameters* XParams)
{
NSISFUNCSTART(hwndNSIS,StrSize,Vars,StackTop,XParams);
MaintainDllSelfRef();
DWORD_PTR Ret=0;
HWND hSrvIPC;
if (GetIPCSrvWndFromParams(hSrvIPC))
if (0==GetWindowThreadProcessId(hSrvIPC,&Ret))Ret=0;
SetVarUINT(INST_0,Ret);
NSISFUNCEND();
}//*/
/*** Unload
Notes: Call in .OnInstFailed AND .OnInstSuccess !
*/
EXPORTNSISFUNC Unload(HWND hwndNSIS,int StrSize,NSISCH*Vars,NSIS::stack_t **StackTop)
{
NSISFUNCSTART4(hwndNSIS,StrSize,Vars,StackTop);
if (!MaintainDllSelfRef())_Unload(); else ASSERT(!"MaintainDllSelfRef failed in Unload!");
NSISFUNCEND();
}
#ifdef _DEBUG
BOOL WINAPI DllMain(HINSTANCE hInst,DWORD Event,LPVOID lpReserved)
#else
extern "C" BOOL WINAPI _DllMainCRTStartup(HINSTANCE hInst,ULONG Event,LPVOID lpReserved)
#endif
{
if (Event==DLL_PROCESS_ATTACH)
{
TRACEF("************************************ DllMain %u\n",GetCurrentProcessId());
ASSERT(!_OpenProcessToken && !_EqualSid);
g.hInstance=hInst;
}
// DBGONLY( if (Event==DLL_PROCESS_DETACH){ASSERT(g.DllRef==0);}TRACE("DLL_PROCESS_DETACH\n"); );//Make sure we unloaded so we don't lock $pluginsdir
return TRUE;
}
//Copyright (C) 2007 Anders Kjersem. Licensed under the zlib/libpng license, see License.txt for details.
#pragma once
/** /#define BUILD_DBGRELEASE // Include simple debug output in release version */
/** /#define BUILD_DBGSELECTELVMODE //Test MyRunAs*/
/** /#define UNICODE // Unicode build */
/**/#define FEAT_CUSTOMRUNASDLG // Include custom runas dialog */
/**/#define FEAT_CUSTOMRUNASDLG_TRANSLATE //*/
/**/#define FEAT_MSRUNASDLGMODHACK // Default to other user radio button */
#if !defined(APSTUDIO_INVOKED) && !defined(RC_INVOKED)
#if (defined(_MSC_VER) && !defined(_DEBUG))
#pragma comment(linker,"/opt:nowin98")
#pragma comment(linker,"/ignore:4078")
#pragma comment(linker,"/merge:.rdata=.text")
//#pragma intrinsic(memset) //http://www.codeguru.com/forum/showthread.php?t=371491&page=2&pp=15 | http://www.ddj.com/windows/184416623
#endif
#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#endif
#ifdef _UNICODE
#define TFUNCSUFFIX W
#else
#define TFUNCSUFFIX A
#endif
#define _PCJOIN(a,b) a##b
#define PCJOIN(a,b) _PCJOIN(a,b)
#define _WIN32_WINNT 0x0501
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
#include <tchar.h>
#include "nsisutil.h"
#ifndef SEE_MASK_NOZONECHECKS
#define SEE_MASK_NOZONECHECKS 0x00800000
#endif
#define COUNTOF(___c) ( sizeof(___c)/sizeof(___c[0]) )
#ifndef ARRAYSIZE
#define ARRAYSIZE COUNTOF
#endif
#if _MSC_VER >= 1400
extern void* __cdecl memset(void*mem,int c,size_t len);
#endif
FORCEINLINE LRESULT MySndDlgItemMsg(HWND hDlg,int id,UINT Msg,WPARAM wp=0,LPARAM lp=0) {return SendMessage(GetDlgItem(hDlg,id),Msg,wp,lp);}
#ifndef UAC_NOCUSTOMIMPLEMENTATIONS
FORCEINLINE HANDLE WINAPI GetCurrentProcess(){return ((HANDLE)(-1));}
FORCEINLINE HANDLE WINAPI GetCurrentThread(){return ((HANDLE)(-2));}
#define MyTStrLen lstrlen
#undef lstrcpy
#define lstrcpy MyTStrCpy
FORCEINLINE LPTSTR MyTStrCpy(LPTSTR s1,LPCTSTR s2) {return PCJOIN(lstr,PCJOIN(cpyn,TFUNCSUFFIX))(s1,s2,0x7FFFFFFF);}
#undef lstrcat
#define lstrcat MyTStrCat
LPTSTR MyTStrCat(LPTSTR s1,LPCTSTR s2)
#ifdef UAC_INITIMPORTS
{return s1?MyTStrCpy(&s1[MyTStrLen(s1)],s2):NULL;}
#else
;
#endif //UAC_INITIMPORTS
#endif //UAC_NOCUSTOMIMPLEMENTATIONS
//DelayLoaded functions:
typedef BOOL (WINAPI*ALLOWSETFOREGROUNDWINDOW)(DWORD dwProcessId);
typedef BOOL (WINAPI*OPENPROCESSTOKEN)(HANDLE ProcessHandle,DWORD DesiredAccess,PHANDLE TokenHandle);
typedef BOOL (WINAPI*OPENTHREADTOKEN)(HANDLE ThreadHandle,DWORD DesiredAccess,BOOL OpenAsSelf,PHANDLE TokenHandle);
typedef BOOL (WINAPI*GETTOKENINFORMATION)(HANDLE hToken,TOKEN_INFORMATION_CLASS TokInfoClass,LPVOID TokInfo,DWORD TokInfoLenh,PDWORD RetLen);
typedef BOOL (WINAPI*ALLOCATEANDINITIALIZESID)(PSID_IDENTIFIER_AUTHORITY pIdentifierAuthority,BYTE nSubAuthorityCount,DWORD sa0,DWORD sa1,DWORD sa2,DWORD sa3,DWORD sa4,DWORD sa5,DWORD sa6,DWORD sa7,PSID*pSid);
typedef PVOID (WINAPI*FREESID)(PSID pSid);
typedef BOOL (WINAPI*EQUALSID)(PSID pSid1,PSID pSid2);
typedef BOOL (WINAPI*CHECKTOKENMEMBERSHIP)(HANDLE TokenHandle,PSID SidToCheck,PBOOL IsMember);
#ifdef FEAT_CUSTOMRUNASDLG
typedef BOOL (WINAPI*GETUSERNAME)(LPTSTR lpBuffer,LPDWORD nSize);
typedef BOOL (WINAPI*CREATEPROCESSWITHLOGONW)(LPCWSTR lpUsername,LPCWSTR lpDomain,LPCWSTR lpPassword,DWORD dwLogonFlags,LPCWSTR lpApplicationName,LPWSTR lpCommandLine,DWORD dwCreationFlags,LPVOID pEnv,LPCWSTR WorkDir,LPSTARTUPINFOW pSI,LPPROCESS_INFORMATION pPI);
#define SECURITY_WIN32
#include <security.h>//namesamcompatible
typedef BOOLEAN (WINAPI*GETUSERNAMEEX)(EXTENDED_NAME_FORMAT NameFormat,LPTSTR lpNameBuffer,PULONG nSize);
#endif
#ifdef UAC_INITIMPORTS
ALLOWSETFOREGROUNDWINDOW _AllowSetForegroundWindow=0;
OPENPROCESSTOKEN _OpenProcessToken=0;
OPENTHREADTOKEN _OpenThreadToken=0;
GETTOKENINFORMATION _GetTokenInformation=0;
ALLOCATEANDINITIALIZESID _AllocateAndInitializeSid=0;
FREESID _FreeSid=0;
EQUALSID _EqualSid=0;
CHECKTOKENMEMBERSHIP _CheckTokenMembership=0;
#ifdef FEAT_CUSTOMRUNASDLG
GETUSERNAME _GetUserName=0;
GETUSERNAMEEX _GetUserNameEx=0;
CREATEPROCESSWITHLOGONW _CreateProcessWithLogonW=0;
#endif
#else
#ifdef FEAT_CUSTOMRUNASDLG
extern GETUSERNAME _GetUserName;
extern GETUSERNAMEEX _GetUserNameEx;
extern CREATEPROCESSWITHLOGONW _CreateProcessWithLogonW;
#endif
#endif /* UAC_INITIMPORTS */
extern DWORD DelayLoadDlls();
#ifdef FEAT_CUSTOMRUNASDLG
extern DWORD MyRunAs(HINSTANCE hInstDll,SHELLEXECUTEINFO&sei);
#endif
#if !defined(NTDDI_VISTA) || defined(BUILD_OLDSDK)
//#if !defined(NTDDI_VERSION) || (NTDDI_VERSION < 0x06000000) || !defined(NTDDI_VISTA)
//#if !defined(TOKEN_ELEVATION_TYPE) || defined(BUILD_OLDSDK)
enum TOKEN_ELEVATION_TYPE {
TokenElevationTypeDefault = 1,
TokenElevationTypeFull,
TokenElevationTypeLimited
};
enum _TOKEN_INFORMATION_CLASS___VISTA {
TokenElevationType = (TokenOrigin+1),
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
};
#endif
#if defined(_DEBUG) || defined(BUILD_DBGRELEASE)
//Simple debug helpers:
#define BUILD_DBG
/** /#define BUILD_XPTEST //Pretend UAC exists and "elevate" with NT runas */
#define DBG_RESETDBGVIEW() do{HWND hDbgView=FindWindowA("dbgviewClass",0);PostMessage(hDbgView,WM_COMMAND,40020,0);if(0)SetForegroundWindow(hDbgView);}while(0)
#define _pp_MakeStr_(x) #x
#define pp_MakeStr(x) _pp_MakeStr_(x)
#define TRACE OutputDebugStringA
#define DBGONLY(_x) _x
#ifndef ASSERT
#define ASSERT(_x) do{if(!(_x)){MessageBoxA(GetActiveWindow(),#_x##"\n\n"##__FILE__##":"## pp_MakeStr(__LINE__),"SimpleAssert",0);/*TRACE(#_x##"\n"##__FILE__##":" pp_MakeStr(__LINE__)"\n");*/}}while(0)
#endif
#define VERIFY(_x) ASSERT(_x)
static void TRACEF(const char*fmt,...) {va_list a;va_start(a,fmt);static TCHAR b[1024*4];if (sizeof(TCHAR)!=sizeof(char)){static TCHAR fmtBuf[COUNTOF(b)];VERIFY(wsprintf(fmtBuf,_T("%hs"),fmt)<COUNTOF(fmtBuf));fmt=(LPCSTR)fmtBuf;}wvsprintf(b,(TCHAR*)fmt,a);OutputDebugString(b);}
#else
#define TRACE /*(void)0*/
#define DBGONLY(_x)
#define ASSERT(_x)
#define VERIFY(_x) ((void)(_x))
#define TRACEF TRACE
#endif /* DEBUG */
#endif /* APSTUDIO_INVOKED */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment