Per-user MSI non-elevated by default - how to show an elevation prompt for writing to a folder? - wix

I have an MSI built via WiX, it's per-user and doesn't display the UAC prompt if the user has rights to the destination folder. However, if the destination folder is in Program Files, it errors with "Insuficient privileges".
How can I show a UAC prompt in the case the destination folder is not writable?

It doesn't really work that way. Per User installs should never require elevation and should never write to Program Files as that's a per machine location. Instead it should install to %LocalAppData%\Programs\Company\Product.
Read the following for a lot more background information. Parts apply and parts may be beyond scope ( dual per-user / per-machine requirements ):
Authoring a single package for Per-User or Per-Machine Installation context in Windows 7

I know this is an old thread, but I want to let others who come across it that it is indeed possible.
As was mentioned in the previous answer, conventional per-user installs should not require admin rights. However, I came across a perfectly valid situation where I needed to run a custom action which required admin rights. Requiring my users to launch the MSI with msiexec from a command line with elevated privileges did not seem like an acceptable solution.
It doesn't seem like this should be so difficult, but luckily I stumbled onto the answer in this post: The Package/#InstallScope attribute doesn’t support per-user, elevated packages! So just omit it in your Package definition:
<!-- NOTE: If you need to create a per-user installation (meaning it's not -->
<!-- visible in Add/Remove Programs from other logons) that prompts for -->
<!-- elevation, omit both the Package/#InstallPrivileges="elevated" and
<!-- Package/#InstallScope="perUser". -->
<Package InstallerVersion="200" Compressed="yes" />
Don't forget to leave ALLUSERS undefined, as well. I described this further on my blog:
How to Elevate a Per User Installer Using WiX

Related

MSI Reinstall Issue with Specified Account already Exists error

We have 2 installer sources in WiX to create installer for a single product with same Product Version, GUID and Package GUID also.
Those 2 installer projects will yield different outputs, one output being just a single MSI file (File1.msi) and other project output is a CD-ROM structure having different MSI file name (File2.msi).
So now issue arises when we installed the product using single MSI file, upon that if we invoke MSI from the other CD-ROM output, we end up getting below mentioned error.
I tried keeping same MSI filename for both kind of installer output, then this above error dialog was resolved but repair functionality isn't working.
If some files were deleted in the product's destination folder, it says source file not found error pointing to CD-ROM installer source folder.
Please help where I'm going wrong. I want to support Repair installation without this errors.
The dialog is expected. You can't change the name of the MSI except during major upgrades.
After that, if you rebuilt to create the different layouts, each MSI probably has a unique PackageCode and that makes them unique packages. That is most likely why repair isn't working. A verbose log file should tell all.
Updated: Compile your main MSI, then run administrative image on it and put the extracted files and MSI on the CD? Put the compressed
version on there as well - just in case they prefer that kind of
release (happens).
I am not sure what will happen when you run both setups this way, but
I think the MSI flagged as an administrative image extract might be
detected by the engine. I am not sure. Should work. Built-in approach for MSI, and you are not fighting wind-mills.
User Accounts: Are you creating any NT User Accounts? Did you set the FailIfExists attribute to yes? Please check here:
User Element (Util Extension). What is the setting for UpdateIfExists? (if any).
Other Issues: There might be other issues as well as Rob mentions. You can not use the same package code for both release types because a package code by definition identifies a unique file. All kinds of X-Files-like problems occur if you try to "hack" this. Not a fight you want to take on.
Administrative Installation: Why would you want to distribute different setups on CDs these days? Corporations that use your setup will run an administrative installation on your setup extracting all files - which is a much better concept. It is essentially a glorified file-extraction, and it is a built in Windows Installer concept intended to make a network installation point for software - among other things. It essentially extracts all files and translates the Media table to use external source files.
List of Links:
What is the purpose of administrative installation initiated using msiexec /a?
Extract MSI from EXE

Is WiX changing the permissions on my Notes.ini file?

I'm using a WiX installer to install a Notes plugin. I use the IniFile action to set the fields in notes.ini telling Notes to load my plugin. I'm using WiX 3.7.1224.0
<IniFile Id="HLBridgeDLLINI" Action="addTag" Directory="LOTUSNOTESINIDIRECTORY" Name="Notes.ini" Section="Notes" Key="AddInMenus" Value="HLBridge.dll"/>
<IniFile Id="HLClientDLLINI" Action="addTag" Directory="LOTUSNOTESINIDIRECTORY" Name="Notes.ini" Section="Notes" Key="EXTMGR_ADDINS" Value="HLClient.dll"/>
Before running the installer, the notes.ini file is writable by Everyone. After the install, the Everyone user is missing from the security attributes. Is WiX doing this, presumably for security reasons? If so, is there a way to disable this? I can write a custom action to change the security back if I have to I suppose.
Short version
Custom permissioning would seem to be applied (unexpectedly) via a WiX element or a custom action during the installation process (other possible causes discussed below - maybe check the major upgrade file revert possibility in particular - or the group policy possibility).
Clues for debugging can be found in the WiX source, or the compiled MSI file, or in a verbose log file (to name a few places to start). Details for each option below.
The below was written very "organically" - it evolved a bit - so it is a bit redundant. I will leave it as it is.
Other Possible Causes
Major upgrade file revert: It is quite odd that the file has less rights after the install. Perhaps this indicates a group policy or a file recreate during installation? The latter sounds very unlikely for such an important file - but it could happen if the update is a major upgrade and the original MSI installed the INI file as a file (instead of as INI file entries) and set it to be a non-permanent file.
In this scenario the INI file will be uninstalled and then reinstalled - likely stripping it of any custom ACL permissioning (ACL permissions are very complicated, they can inherit and override, and deny or grant, etc...). Any custom INI entries added to the old file will also be wiped out - check for such missing custom entries after installation.
This is a common problem (major upgrade file revert): major upgrade file uninstall and reinstall making the file appear reverted or overwritten when it has been wiped out and installed fresh instead and can trigger many other problems than ACL issues.
Other potential sources for the unexpected permissioning are also possible:
repair / modify operation for another Lotus Notes-related MSI package targeting the same INI file?
another MSI run as part of the same setup bundle doing permissioning?
group policy / active directory processes enforcing standard ACLs? (sample)
an executable / service run in admin mode doing something funky?
scheduled tasks interference? (some possibilities)
logon scripts doing something funky? (very unlikely in your case, but login scripts can do pretty much "anything" - and they do)
some other, unexpected source. Something with admin rights does this - that is the obvious common denominator.
My 2 cents: if this is an in-house, corporate package, use group policy to apply permissioning instead and remove the operation from your package (unless you deploy to computers outside group policy control - but then you can have a special package which only does permissioning and keep permissioning out of your main package - making it less error prone).
ACLs
The problem you describe is very interesting. I am not aware of anything automatic in WiX that would meddle with ACLs, though I can not guarantee it. There are, however, constructs that are designed to change ACLs when you specify them explicitly - and you need to check your MSI for these constructs (described below)
But first of all: I ran a quick smoke test with a WiX MSI to see if I could replicate the problem, and I can not replicate it. My fear was that this could be something changed in a recent Windows Update. In other words some sort of security fix distributed without anyone's awareness which changes core functionality in Windows Installer (it wouldn't be the first one).
ACL-permissioning
Some info on how ACL permissioning can be implemented in your MSI. Essentially you can use ready-made WiX elements, or run your own custom action.
There are several WiX elements that deal with ACL-permissioning and they result either in settings added to standard, built-in MSI tables or they add entries to custom WiX tables. Look for these elements in your WiX source (if available) (samples):
Permission (maps to built-in, standard MSI LockPermissions table).
PermissionEx (WiX-specific Util extension permissioning - maps to custom WiX table).
PermissionEx (maps to built-in, standard MSI MsiLockPermissionsEx table - a feature added in Windows Installer version 5).
FileSharePermission (WiX-specific Util extension file share permissioning - maps to custom WiX table).
I am not sure why the WiX guys decided to support all these different permissioning options - there are surely good reasons - since it must be a lot of work to maintain for them. I have written permissoning code myself, and in my view it is a time bomb of conspiratory complexity to deal with. Permissions permute like you wouldn't believe, but that is off topic here. In my condensed view very few permissions make any sense, but full flexibility is allowed by ACL permissioning - all the rope you need to shoot yourself in the foot. I prefer the generic "macros": GenericAll="yes", GenericExecute="yes", GenericRead="yes", GenericRead="no", etc...
Additionally you can use custom actions to call command line permissioning tools such as subinacl.exe, cacls.exe, xcacls.exe, icacls.exe or several other ones - which I would definitely not recommend for reliability and security reasons. Custom actions are never preferable when there are other options: Why is it a good idea to limit the use of custom actions in my WiX / MSI setups?
The Permission element I would not use for technical reasons, the built-in MsiLockPermissionsEx table I have never tested. The WiX-specific PermissionEx element is probably what I would choose to use if I needed this ACL permissioning at all.
Inspect MSI
If you have WiX source access, you should be able to find the permissioning elements or the custom action elements that cause the problem.
However, if you do not have WiX source access, you can also check your actual, compiled MSI file for any custom features that could apply custom permissioning. I would focus on the Custom Action table and any custom WiX / MSI tables found in the MSI in question.
In other words: inspect the compiled MSI file used for installation for custom actions and custom tables that are used to set ACLs. See MSDN for a list of standard MSI tables. Any table you don't find there is custom.
To inspect the MSI, use Orca or an equivalent tool. See this answer (towards bottom) for a list of tools you can use (commercial or free): How can I compare the content of two (or more) MSI files?
Verbose Logging
You can also do what I always do: create a proper, verbose log for the MSI install in question. This gives you something to start with to figure out what is happening - and as such it might in some cases be better than just inspecting the MSI. You can find some information on how to do logging here.
Alternatively, you can enable logging for all MSI installations. See installsite.org on logging (section "Globally for all setups on a machine") for how to do this. I prefer this default logging switched on for dev and test boxes, but it does affect installation performance and adds a lot of log files to the temp folder (that you can just zap once in a while). Typically you suddenly see an MSI error and you wish you had a log - now you can, always ready in %tmp%.
I would also make a note of what OS you are on, and determine if the problem is seen only on this OS? And this also involves figuring out if you have the latest hotfixes installed.

WIX/MSI setup writes to HKLM despite MSIINSTALLPERUSER=1

I try to create a MSI Single Package using WIX (3.10). The user can select in the UI whether the package should install Per-User or Per-Machine. The package is initialized with ALLUSERS=2 and MSIINSTALLPERUSER=1. I did not set an InstallScope, as this would limit the Package to one or the other.
If the user selects Per-Machine the setup continues with ALLUSERS=2 and MSIINSTALLPERUSER="". - Everything works as expected.
If the user chooses Per-User installation ALLUSERS=2 and MSIINSTALLPERUSER=1 remain unchanged. If a non-privileged user runs the setup everything works as expected as well.
But if a privileged user executes the setup and chooses Per-User installation, registry keys that should be written to HKCU\Software\Classes\ are still written to HKCR / HKLM\Software\Classes\. This happens without UAC being invoked.
I observed this behavior on a Windows 8.1 (current patch level).
This is not what I want. If the user selects Per-User installation, there should be nothing written to HKLM/HKCR.
Here is sample from the wxs-file:
<Component Id="MyRegistryComponent" Guid="{99999999-9999-9999-9999-999999999999}">
<RegistryValue Id="MyRegistryComponent_MainKey" Root="HKCU" Key="Software\Classes\myapp.myclass" Value="myapp.myclass.foo" Type="string" />
</Component>
I checked the MSI with Orca. This RegistryValue has Root=1 in the Registry table of the MSI.
I tried/checked already:
I verified that the ALLUSERS and MSIINSTALLPERUSER properties are
indeed set as stated above.
I tried setting ALLUSERS="" MSIINSTALLPERUSER=1 – No effect.
I changed Root="HKCU" to Root="HKMU" in the wxs.
This results in Root=-1 in the MSI, but I does not change the final
result after installation.
What am I missing here?
Is possible that this behavior is caused by previous incomplete (un-)installations?
When you say "This results in Root=-1 in the MSI, but I does not change the final result after installation." it's not clear which of the results you're referring to but:
HKCU will always go to HKCU, HKMU is what you need to switch between HKCU and HKLM in a single package setup. So using HKMU and producing a verbose log would be useful to see if there is a failure somewhere in there.
You will get changes in HKCR. That's usually the normal behavior because as this article explains:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms724475(v=vs.85).aspx
Quote "The HKEY_LOCAL_MACHINE\Software\Classes key contains default settings that can apply to all users on the local computer. The HKEY_CURRENT_USER\Software\Classes key contains settings that apply only to the interactive user. The HKEY_CLASSES_ROOT key provides a view of the registry that merges the information from these two sources. HKEY_CLASSES_ROOT also provides this merged view for applications designed for previous versions of Windows."
HKCR is a virtual view that combines class registration for the current user with class registration for the machine to present a view of all class entries on the system. Other accounts (such as the system account) will see only HKLM, so what you see exactly depends on who you are.
I suggest that is what is needed here is a test case that uses HKMU as is required, and maybe post that test case. Keep in mind that seeing HKCR entries is normal, going by the documentation, so maybe that explains all that you are seeing.

Wix Installer: Write to HKLM/HKCR in per-user installation without elevated rights

I created a "perMachine" installer with the wix toolset. The installer has to be started with admin rights, because it writes multiple registry keys to HKLM and HKCR.
I'd like to change the installer to a "perUser" installation. Is it possible to do this and still write to HKLM and HKCR? The registry entries have to be there to register a shell icon overlay handler...
Greets
EDIT:
There does not seem to be a way to write to HKLM as non-admin user.
But is there a way to get wix installer to promt for admin pw? If so, I could live with the following solution:
user knows admin pw (or admin provides pw): the icon overlay component is installed
user does not provide admin pw: the icon overlay component is skipped.
So my questions are:
1. How to promt the user for the admin pw?
2. How to skip/install a component based on the users action?
EDIT2:
I have tried multiple combinations of:
InstallPrivileges="elevated"
InstallScope="perUser"
Property "ALLUSERS" set to empty string
Property "MSIUSEREALADMINDETECTION" set to 1
Property "MSIINSTALLPERUSER" set to 1
Condition for said component: "Privileged / AdminUser"
Nothing has worked so far. It all comes down to the Condition not beeing read correctly. I have tried on several PCs with an admin user. In order to get the product to install with ALL features, I have to start the installer explicitly as administrator over the context menu. Otherwise the icon overlay handler won't get installed.
Pointing out something that the OP doesn't seem to understand fully...
Per machine and per user have nothing to do with the privilege required to write to HKLM. Windows Installer won't break security rules either way. If the install requires privilege and doesn't have it, then it fails in both cases. In WiX, the Package InstallPrivileges is what tell Windows whether to elevate so that the install will work. If the installing user doesn't have the privilege the elevation prompt will allow entry of admin credentials. So the answer to your 1 is that InstallPrivileges=elevated should allow entry of an admin account if the user is not admin. otherwise a plain elevation.
You could try a condition on the components containing the restricted registry entries, try a condition of Privileged so they install only if the install is elevated. I have never tried this, but it should work.
https://msdn.microsoft.com/en-us/library/aa370852(v=vs.85).aspx
Other alternatives are:
Group Policy will allow the setup to be pushed to machines of limited users. This is common in corporate environments.
It occurs to me that the shell icon overlay function might be available on a user by user basis. This is a SWAG, and I'm not aware of all the registry entries required, but you can certainly register your COM classes in HKCU, for example. If you use the WiX HKMU key, the items will be created for the machine in a per machine and for the user in a per user. An example here:
http://www.merlinia.com/mdt/WiXTutorial1.msl
No, a per-user install has write-access to only per-user resources like the registry and c:\users when configured for per-user. This has the effect in Vista and later of not requiring elevation and thus not requiring UAC.

Can a .msi file install itself (presumably via a Custom Action)?

I wand to construct an MSI which, in its installation process, will deploy itself along with its contained Files/Components, to the TargetDir.
So MyApp.msi contains MyApp.exe and MyAppBootstrapperEmpty.exe (with no resources) in its File Table.
The user launches a MyAppBootstrapperPackaged.exe (containing MyApp.msi as a resource, obtained from the internet somewhere, or email or otherwise). MyAppBootStrapperPackaged.exe extracts MyApp.msi to a temp folder and executes it via msiexec.exe.
After the msiexec.exe process completes, I want MyApp.msi, MyBootstrapperEmpty.exe (AND MyApp.exe in %ProgramFiles%\MyApp folder so MyApp.exe can be assured access to MyApp.msi when it runs (for creating the below-mentioned packaged content).
MyAppBootstrapper*.exe could try and copy MyApp.msi to %ProgramFiles%\MyApp folder, but would need elevation to do so, and would not allow for its removal via Windows Installer uninstall process (from Add/Remove Programs or otherwise), which should be preserved.
Obviously (I think it's obvious - am I wrong?) I can't include the MSI as a file in my Media/CAB (chicken and egg scenario), so I believe it would have to be done via a Custom Action before the install process, adding the original MSI to the MSI DB's Media/CAB and the appropriate entry in the File table on the fly. Can this be done and if so how?
Think of a content distribution model where content files are only ever to be distributed together with the App. Content is produced by the end user via the App at run time, and packaged into a distributable EXE which includes both the App and the content.
MyApp's installer must remain an MSI, but may be executed by a Bootstrapper EXE. The installed MyApp.exe must have access to both MyApp.msi and EXE is to be "assembled" at runtime by the App from a base (empty) MyAppBootstrapper.exe, which is also installed by the MSI, and the content created by the end-user. The EXE's resource MSI must be the same as that used to install the App which is doing the runtime packaging.
WIX is not to be installed with MyApp.
There can be no network dependencies at run-/packaging- time (i.e. can't do the packaging via a Webservice - must be done locally).
I am familiar with (and using) Custom Actions (managed and unmanaged, via DTF and otherwise).
Add an uncompressed medium to your wxs like this:
<Media Id='2'/>
And then create a component with a File element like this:
<File Source='/path/to/myinstaller.msi' Compressed='no' DiskId='2' />
This will make the installer look for a file called "myinstaller.msi" on the installation medium, in the same folder as the msi that is being installed. The source path above should point to a dummy file, it is only there to appease wix.
Edit: The following sample test.wxs demonstrates that it works. It produces a test.msi file which installs itself to c:\program files\test. Note that you need to put a dummy test.msi file in the same folder as text.wxs to appease wix.
<?xml version='1.0' encoding='utf-8'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Product
Name='ProductName'
Id='*'
Language='1033'
Version='0.0.1'
Manufacturer='ManufacturerName' >
<Package
Keywords='Installer'
Description='Installer which installs itself'
Manufacturer='ManufactererName'
InstallerVersion='100'
Languages='1033'
Compressed='yes'
SummaryCodepage='1252'/>
<Media Id='1' Cabinet='test.cab' EmbedCab='yes'/>
<Media Id='2' />
<Directory Id='TARGETDIR' Name="SourceDir">
<Directory Id='ProgramFilesFolder'>
<Directory Id='TestFolder' Name='Test' >
<Component Id="InstallMyself">
<File Source="./test.msi" Compressed="no" DiskId="2" />
</Component>
</Directory>
</Directory>
</Directory>
<Feature
Id='Complete'
Display='expand'
Level='1'
Title='Copy msi file to program files folder'
Description='Test'>
<ComponentRef Id="InstallMyself" />
</Feature>
</Product>
</Wix>
Having one .MSI package launch another .MSI package from "within" itself is called a nested install, and it's bad juju (see Rule 20). Windows Installer has some global data that it uses to manage the current install, and it doesn't handle well multiple installs at the same time. For the same reason, if you start one install and then try to start another while the first is still in progress, you'll usually see a pop-up to the effect of "another install in progress, please wait until it's done".
You can have a program, usually called a bootstrapper (I think that's what you're referring to) which is itself not an install package, but which contains an install package (such as an .MSI or an .EXE) as a resource, possibly compressed. The action of the bootstrapper program is to extract/expand the resource to a file, commonly in a %TEMP% directory, then either launch the extracted .EXE or run MSIEXEC on the extracted .MSI. The bootstrapper can contain multiple resources and extract+install them one by one, if you need to install prerequisites before the main package. Or you can ship multiple packages as separate files, and have the bootstrapper execute/install them directly from the distribution media one by one, or copy them down to the target machine and run the series of install from there, or...
WiX itself does not get installed, no. It's a tool with which .MSI packages can be built. The WiX project has on its wishlist a generic bootstrapper program, but it hasn't been implemented yet. There are other bootstrappers available, e.g. this one.
You won't need a custom action -- in fact, since the bootstrapper isn't itself a Windows Installer installation package, "custom action" has no meaning to it. And, if you're familiar enough with CAs to know about managed/unmanaged/DTF, then you know enough to avoid custom actions whenever you can. (grin)
I think it's much easier for your bootstrapper to extract MSI file to some predefined location rather than to the temp folder. For example, to C:\Documents and Settings\All Users\Application Data\My Company\My Product Install Cache. After installation finishes bootstrapper would leave MSI file sitting there. If at some stage user decides to reinstall your product Windows Installer will be able to locate source MSI file.
Also, add path to this file to RemoveFile table so that it gets deleted on uninstall. You can use RemoveFile element in WiX for that.
So if I understand, then I think I would have the app create a transform (MST) that has the content files and apply that to the base MSI. I'm still not convinced that I understand though. :)
I'd configure the MSI cache path to a known location.
Then at runtime if you need to "edit" the MSI use VBScript or similar.
But still, I ask WHY!?!
I am also working on a way to deploy multiple MSI files. I have a bootstrapper.exe program that bundles the MSI files and runs them one at a time. This solves my problem for most cases.
The case it does not solve is GPO (Global Policy Object) distribution of the install. GPO requires a dot-msi file to run an install.
To do this here's what I did which almost solved the problem (but not quite). I put the dot-msi files in the file table of an installer and put my bootstrapper in the binary table and run it from a custom action inserted after InstallFinalize in the InstallExecuteSequence. Of course the bootstrapper won't be able to run other MSI's because the top level MSI holds the _MSIExecute mutex.
It was pretty easy to get a little further. I made the bootstrapper return control to the top level installer and continute. And then I added a WaitForSingleObject call to wait for the top level install to finish, and the bootstrapper can then continue to finish the install.
My problem is that the GPO installation happens at boot time and the top level install completes before the sub installers are done and GPO reboots the machine.
The toplevel install also returns a success status when the install may actually fail later on.
I'm still looking for a way to block the top level install from completing until after the bootstrapper completes.