Wix elevated AND impersonating Custom Action - wix

I have a custom action that needs to modify registry settings:
it is marked as deferred in order to support a rollback
it must be run with elevated priviliges in order to modify the Registry
it must be run within the security context of a particular user (impersonated)
<CustomAction Id="EncryptRegistrySettings"
BinaryKey="DLServiceCA"
DllEntry="EncryptRegistrySettings"
Execute="deferred"
Impersonate="yes"
Return="check"/>
I have some other ugly options to workaround this problem (Execute="immediate" or impersonate="yes" with elevated subprocess) which I don't want to resort to.
The problem is: with Impersonate="no" subprocess msiexec is run by SYSTEM. With yes it is run by User, but not elevated.
So the question is:
Is it possible to run a Custom Action both as Impersonated AND elevated?

No, they are mutually exclusive capabilities in Windows Installer.

Related

Executing wix msi custom actions as system

I am using WIX to build a an MSI that will be executed as a regular user but with system privileges (AlwaysInstallElevated=1). I have defined two Custom Actions that execute net.exe.
The net.exe commands are not getting executed as a regular user. I have also tested executing this msi as an Administrator and the net.exe commands are getting executed.
I have logged the msi output and I see error codes that lead me to believe that the net.exe commands are not being executed elevated. I'm reaching out to the community to see if
What I'm trying to do is possible
Do I need to instead use an exe as a custom action in order for the exe to install elevated.
Thanks in advance for the feedback.
<CustomAction Directory="TARGETDIR" ExeCommand="[SystemFolder]net.exe user TestUser /add" Return="ignore" Execute="deferred" HideTarget="no" Impersonate="no" Id="Command1">Command1</CustomAction>
<CustomAction Directory="TARGETDIR" ExeCommand="[SystemFolder]net.exe localgroup Administrators TestUser /add" Return="ignore" Execute="deferred" HideTarget="no" Impersonate="no" Id="Command2">Command2</CustomAction>
<InstallExecuteSequence>
<Custom Action="Command1" After="PublishProduct">NOT Installed</Custom>
<Custom Action="Command2" After="Command1">NOT Installed</Custom>
</InstallExecuteSequence>
Machine Policy and User policy allows for msis to be executed as system.
MSI (c) (DC:F8) [09:17:39:438]: Machine policy value 'AlwaysInstallElevated' is 1
MSI (c) (DC:F8) [09:17:39:438]: User policy value 'AlwaysInstallElevated' is 1
MSI (c) (DC:F8) [09:17:39:438]: Running product '{34ED8E61-40EA-47CE-95E7-8EE3CDBCB1E8}' with elevated privileges: All apps run elevated.
Errors
MSI (s) (8C:8C) [20:39:18:989]: Executing op: ActionStart(Name=Command1,,)
MSI (s) (8C:8C) [20:39:18:989]: Executing op: CustomActionSchedule(Action=Command1,ActionType=3170,Source=C:\,Target=C:\WINDOWS\SysWOW64\net.exe user TestUser /add,)
CustomAction Command1 returned actual error code 2 but will be translated to success due to continue marking
MSI (s) (8C:8C) [20:39:19:535]: Executing op: ActionStart(Name=Command2,,)
MSI (s) (8C:8C) [20:39:19:535]: Executing op: CustomActionSchedule(Action=Command2,ActionType=3170,Source=C:\,Target=C:\WINDOWS\SysWOW64\net.exe localgroup Administrators TestUser /add,)
CustomAction Command2 returned actual error code 1 but will be translated to success due to continue marking
Even in an install started by an administrator the impersonated custom actions run without elevation. So the answer is your number 2. start the install from an elevated process with a CreateProcess-type of initiation. Having said that, it's not clear to me why you can't run the CA deferred with no impersonation because there seems to be nothing that requires the user to be an actual user as opposed to the system account. So the failure might not be elevation, and that's the issue with passing the task off to a program that isn't going to give you good error messages. So....
I've seen boilerplate code to do this kind of thing, and I believe WiX has it anyway, the Util User element, that may be the way to go.
Like Phil says, don't use net.exe to create users. Use WiX's built in features to do so (I should have found a better sample but don't have time right now):
http://wixtoolset.org/documentation/manual/v3/xsd/util/user.html
https://www.firegiant.com/wix/tutorial/com-expression-syntax-miscellanea/new-user-on-the-block/
My first instinct would be that the AlwaysInstallElevated policy would be misconfigured, but judging from the log file it is set correctly. I am rusty, but the impersonation looks like it is correctly set to "no" Phil? And the scheduling is deferred and seemingly placed right before InstallFinalize - seems OK off the top of my head.
Could it be that net.exe cannot be run properly as LocalSystem? Why then would it work when the installation is kicked off as Administrator? One would think LocalSystem had all required privileges. Isn't the current user's access token attached to the msiexec.exe process somehow? (for logging purposes or something). Or could it be a privilege that the Administrator account has that LocalSystem does not? How would that privilege apply if there is no impersonation? Did you try to manually launch net.exe as a regular user? What errors do you get when attempting to create a user when running as a regular user?
In either case I wouldn't waste my time running EXE custom actions if there is an alternative. I don't remember the last time I did. I normally use VBScript, C++ and earlier I used Installscript - with all its archaic syntax. Just switch to the built-in WiX constructs and you should be fine.

Invoking Data Protection API from within a CustomAction

I am trying to run some unmanaged code from a C# ca within WIX. I have a wrapper around the CryptProtectData function, this code works fine if run from an admin prompt. I have scheduled the ca as deferred hoping it would run during the elevated session however it fails in this context.
The scheduling is as follows:
<CustomAction Id="SetCustomActionDataValue" Property="EncryptValues" Value="foo=bar;...;" />
<CustomAction Id="EncryptValues" BinaryKey="InstallerCustomAction.CA.dll" DllEntry="EncryptValues" Execute="deferred" Impersonate="no" HideTarget="no" Return="check" />
<InstallExecuteSequence>
<Custom Action="SetCustomActionDataValue" After="InstallFiles" />
<Custom Action="EncryptValues" After="SetCustomActionDataValue" />
</InstallExecuteSequence>
What needs to be done to allow something like this to run during the installer, preferably after the files and registry entries are committed to the system?
Do you have any logging in your CA? Can you confirm that your CustomActionData made it over?
One thing you might try is to wire this up as a simple console app and use PSExec to invoke a cmd.exe prompt as SYSTEM.
I've never used this API but the comment "Typically, only a user with the same logon credential as the user who encrypted the data can decrypt the data." makes me think that this might not run well as SYSTEM. This article seems to confirm that. http://www.obviex.com/samples/dpapi.aspx
FWIW, I'm not sure what you are trying to achieve but I'd consider moving it from the installer to the application if possible. Installers are generally best kept simple and reliable.

Remove popups that are caused by custom actions - Wix

Newbie to wix, I am learning it. I had initially set up some custom actions for installing/ uninstalling/ rollback actions. These custom actions cause unintended popups during installation. I am trying to remove the same using SetProperty, DllEntry and BinaryKey. This is my original code:
<CustomAction Id="InstallStorageService"
Directory="ProductAppDataFolder"
ExeCommand='[ProductAppDataFolder]bin\install_service.bat "[InstallationFolder]" "[ProductAppDataFolder]" "$(var.StorageServiceName)"'
Execute="deferred"
Impersonate="no"
Return="check"/>
This is what I have changed it to:
<SetProperty Id="InstallStorageService"
Value="cmd.exe /c [PRODUCTAPPDATAFOLDER]bin\install_service.bat "[InstallationFolder]" "[ProductAppDataFolder]" "$(var.StorageServiceName)""
After="CostFinalize"/>
<CustomAction Id="InstallStorageService"
BinaryKey="WixCA"
Execute="deferred"
Return="check"
DllEntry="CAQuietExec"
Impersonate="yes"/>
I have done similarly for removing and for rollback actions. I do not get any errors or issues while I run the .bat file to create the msi file, but once the msi is created, I am having trouble installing it. I can share the log file if required, but there are fatal errors with not much of verbose about it.
Any help would be greatly appreciated.
You can use the Quiet Execution Custom Action pattern to invoke out of process exe's and scripts. The pattern will capture stderr and stdout to the Windows Installer log.
However it would be better to not use a .BAT as self registration (in all forms) is not a windows installer best practice. See Zataoca: Custom actions are (generally) an admission of failure.

Run ExeCommand in customAction as Administrator mode in Wix Installer

I am new to wix installer. I have developed a set-up using wix installer for my application and I need to execute a Custom Action to run a command in cmd.exe. In XP it works fine. But in Windows 8 & 7 the cmd prompt needs to be run as administrator.
I have googled and found the keywords Elevated Privileges and impersonate might help me.
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" InstallPrivileges="elevated"></Package>
As you can see above, I used the InstallScope attribute set to perMachine, and I have used Impersonate="No" in the CustomAction element:
<CustomAction Id='comReg' Directory='INSTALLLOCATION' Impersonate='no'
ExeCommand='"[NETFRAMEWORK40CLIENTINSTALLROOTDIR]regasm.exe" "[INSTALLLOCATION]myProduct.dll" /codebase' Return='check' />
But I didn't get any changes while installating. I need the command prompt to open and run the above command in Administrator Mode.
And can anyone please tell me about these keywords "Elevated Privileges & impersonate"
<InstallExecuteSequence>
<Custom Action='comReg' After='InstallFinalize'>NOT REMOVE</Custom>
<Custom Action='comUnreg' Before='RemoveFiles'>REMOVE</Custom>
</InstallExecuteSequence>
How to do it?
The wix documentation here explains the Impersonate attribute:
This attribute specifies whether the Windows Installer, which executes as LocalSystem, should impersonate the user context of the installing user when executing this custom action. Typically the value should be 'yes', except when the custom action needs elevated privileges to apply changes to the machine.
You also need to understand the difference between deferred and immediate custom actions. See the Execute attribute on the same help page:
This attribute indicates the scheduling of the custom action. This attribute's value must be one of the following:
deferred
Indicates that the custom action runs in-script (possibly with elevated privileges).
immediate
Indicates that the custom action will run during normal processing time with user privileges. This is the default.
At present your custom action is immediate, which means it is running with user privileges. See this blog post for lots of details, but particularly:
Any immediate custom actions impersonate the invoking user. Before Windows Vista this wasn't a problem since at this point the installing administrative user had a privileged token. With the introduction of UAC in Windows Vista the default administrative token with UAC enabled is a filtered token and does not hold all privileges. Since immediate custom actions are not supposed to modify machine state - only to gather state data and schedule custom actions to run deferred - this still shouldn't be a problem. After all, at this point the generation of the installation and rollback scripts is all that should be going on.
You should never modify machine state with an immediate custom action. Use a deferred one, and keep impersonate to no, and it should work:
<CustomAction Id='comReg' Directory='INSTALLLOCATION' Execute='deferred' Impersonate='no' ExeCommand='"[NETFRAMEWORK40CLIENTINSTALLROOTDIR]regasm.exe" "[INSTALLLOCATION]EAWordImporter.dll" /codebase' Return='check' />
EDIT: Schedule the custom action using the InstallExecuteSequence element:
<InstallExecuteSequence>
<Custom Action='comReg' Before='InstallFinalize'/>
</InstallExecuteSequence>

PerUser install with Custom Action - UAC disabled

I'm using WiX to generate an MSI that installs a browser plugin on a perUser basis. I have a custom action to install a driver using DPInst (which needs elevated privileges).
The install works fine when UAC is enabled; Windows shows the prompt to elevate. However, if I have UAC disabled and try to install on a regular user account, dpinst.exe will get spawned until the computer freezes. (About a thousand times at last count).
In the <InstallExecuteSequence> I have:
<Custom Action="Install_Drivers" After="InstallFiles">NOT Installed</Custom>
My custom action is:
<CustomAction Id='Install_Drivers' Execute='deferred' Directory='DRIVERS' ExeCommand='"[DRIVERS]DPinst.exe" /SW /SA' Return='ignore' Impersonate='no'/>
I have Return='ignore' because, from what I understand so far, dpinst.exe always returns a non-0 code.
How can I ensure that the custom action fails correctly when UAC is disabled? On a related note, can I show a custom message to the user when the driver installation fails?
Further Information: I'm developing on Windows 7 currently, but targeting WinXP and up.
Edit Taking a look at the log for the installation these seem to be the relevant lines:
Executing op: CacheSizeFlush(,)
Executing op: ActionStart(Name=Install_Drivers,,)
Executing op: CustomActionSchedule(Action=Install_Drivers,ActionType=3170,Source=C:\long_redacted\Drivers\,Target="C:\long_redacted_path\Drivers\DPinst.exe" /SW /SA,)
Disallowing shutdown. Shutdown counter: 0
CustomAction Install_Drivers returned actual error code 1073807364 but will be translated to success due to continue marking
The bit about the shutdown is, I believe, when I logged off stop the installation. (Canceling doesn't seem to have any effect).
Try setting the 'Impersonate=no' attribute on the 'CustomAction' element, like this:
<CustomAction Id='Install_Drivers' Execute='deferred' Directory='DRIVERS' ExeCommand='[DRIVERS]DPinst.exe" /SW /SA' Return='ignore' Impersonate="no" />
Also note: you have a stray double-quote in your ExeCommand
Installing a driver is an inherently per-machine operation. A limited user can't do it. So with UAC disabled, it's not going to work. DPInst apparently doesn't get the hint that it doesn't have permissions and can't get them. Sounds like a bug in DPInst. You should change your installer to be per-machine and add a launch condition on the Privileged property to prevent the installer from starting for limited users without UAC.