Restrict access to a folder installed using wix installer - wix

My use case is to run a msi package which creates a folder, in addition to doing other things, which should be accessible only to the Administrators. I tried using the util:PermissionEx element but the wix page for the PermissionEx element doesn’t really tell about what do different attributes do. I tried various things, one eg below, but I was still able to access the created folder from both administrator and a non-admin user account.
<Directory Id=“TmpDir” Name=“TmpDir”>
<Component Id="CreateDir" Guid=“<>">
<CreateFolder>
<util:PermissionEx User="Users" Read="no" GenericRead="no”/> <!— Tried different combinations here —>
</CreateFolder>
</Component>
</Directory>
Any help as to what should I do to achieve what I have stated above. Let me know if my question is unclear and I can explain in a more detail or in a different way. Also the Package has
InstallScope="perMachine"
InstallPrivileges="elevated"

Related

WiX Installer - Children of LocalAppDataFolder installed to wrong location after UAC prompt

Brand new to WiX and I'm trying to fix a bug in some software I took over recently. As part of the product definition we have certain files being installed to the user's "AppData\Local" directory. The current project setup works fine when the user has install privileges, with the files being placed in the expected users "AppData\Local" directory.
However, if the UAC prompt pops up requiring admin credentials, the files end up being install in the admin account's "AppData\Local" directory instead.
In the product file we have something like:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="LocalAppDataFolder">
<Directory Id="MyData" Name="My Data">
...
</Directory>
</Directory>
</Directory>
And then in a heat.exe generated file I have
<Fragment>
<DirectoryRef Id="MyData">
<Directory Id="UserMaps" Name="User Maps" />
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="User_Maps_Components">
<Component Id="Map1.png" Directory="UserMaps" Guid="{C27...autogenerated}">
<File Id="Map1.png" Source="$(var.MapPath)\Map1.png" KeyPath="no" />
<RegistryValue KeyPath="yes" Root="HKCU" Key="Software\MyApp\Usermaps" Type="integer" Value="1" Name="Map1.png" />
</Component>
</ComponentGroup>
</Fragment>
Is there a way to ensure that the files will be installed to the initiating user's directories, rather than the admin's account?
We have files installing to "Program Files" as well, so I could always install the files there and copy them over as part of my applications initialization logic if they aren't found in the active user's folders, but was interested in seeing if there was a WiX specific way of doing things.
I assume you are actually entering an admin password and specifying an admin account to run the setup with? That is as opposed to elevating the admin account you are logged in with. The latter should make the file install in the expected folder. The former would install like you describe.
I would roll with the approach you describe yourself: copy the file in place to each user profile from a copy installed per machine. I wrote up some suggestions for user profile and HKCU deployment a while back. Just some thoughts on the subject - a subject which seems to have few good answers. The Active Setup approach is apparently no longer supported in Windows 10.
Throwing in Some Links:
Why is it a good idea to limit deployment of files to the user-profile or HKCU when using MSI?
Create a .config folder in the user folder
C++ MSI Package Administative Privileges
System.UnauthorizedAccessException while running .exe under program files

How can i identify the current logon user in Wix toolset?

I'm trying to build an installer with Wix and i have to put a file in the Startup folder. I already found out how to build the path to the startup folder but i can't find any variabile that can identify the current user.
That's what i have done for now and it works but the part with the name of the user just creates a new directory with that name
There are a couple of things probably wrong with your idea:
You shouldn't need to build that entire directory tree to get to the StartupFolder because there is already a standard Windows Installer property named StartupFolder. This is already the path to the current user's startup folder so it's not clear why you need the value of LogonUser.
Properties are resolved by placing them in square brackets, so in the general case you'd use [LogonUser], but directory names in the Directory table are not marked as Formatted type, so calling a directory [LogonUser] won't work. You'd need to set another public property to the value of [LogonUser] and then use that property as a directory name. However, I think point 1. may be all you need, and your directory tree isn't clear about your intent.
Run on Startup
Normally you would put a shortcut to a file in the startup folder, and not an actual file. You do so by refering to the built-in Windows Installer Property StartupFolder as shown in the mockup-sample below (and as stated by Phil).
In the realm of alternatives, there are many ways to schedule something to start with Windows. What type of file is this and what does it do? In case you are interested, you can see a number of ways used to start something on login or boot by running AutoRuns (from SysInternals). There is a shocking array of possibilities (small digression).
Very often you can run things as services or scheduled tasks, rather than using other startup features. Generally services for features that need to run continuously, and scheduled tasks for stuff that needs to run every now and then. I think most people want to avoid too many things running on login - if they are not really necessary. I find the startup folder "clunky" - and also prone to user interference as well.
Self-Repair and the Startup Folder
This Experts-Exchange article describes a case when self-repair was triggered after deleting a startup folder entry (search for "startup" to find the section).
Frankly I am a bit surprised at the described scenario. When a shortcut is deleted, it should not come back automatically easily, since it is generally not the key path of its hosting component. Still, something to check when you test your MSI (delete the shortcut and then launch your app directly - if there is a shortcut to do so). If you see the problem, please let us know.
If I were to guess what really happened, they might have installed an actual file into the shortcut folder and set it as the key path (which is what it seems you are trying to do as well). Then they have put this in the same feature hierarchy as an advertised shortcut - the same feature or the top feature of the application, or a parent feature - causing self-repair to always be invoked when the advertised shortcut is invoked, and the missing file is detected in the Startup folder and self-repair ensues.
Digression: a sizeable digression, the important point is to please check this for your setup! This kind of problem really aggravate your users - the cause of it tends to elude their support guys.
Mockup WiX Sample
Here is one sample for how to install a shortcut to the Startup folder. Note that the Startup folder redirects depending on whether the setup is installed per-user or per-machine, as documented on MSDN: StartupFolder.
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="Startup Shortcut" Manufacturer="Someone" Version="0.0.1"
Language="1033" UpgradeCode="PUT-GUID-HERE">
<Package InstallScope="perMachine" Compressed="yes" />
<Media Id="1" Cabinet="my.cab" EmbedCab="yes" />
<UIRef Id="WixUI_Mondo" /> <!-- Just include a default setup GUI -->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder" Name="PFiles">
<Directory Id="MyCompany" Name="Company">
<Directory Id="MyAPP" Name="MyApp">
<Component Feature="MyFeature">
<File Source="MyApp.exe" />
<!-- Set Advertise="no" to avoid advertised shortcut -->
<Shortcut Id="MyApp" Directory="StartupFolder" Name="MyApp"
Advertise="yes" />
</Component>
</Directory>
<Directory Id="StartupFolder" />
</Directory>
</Directory>
</Directory>
<Feature Id="MyFeature" Absent="disallow" />
<Property Id="MSIFASTINSTALL" Value="7" /> <!-- Tweak to install faster -->
</Product>
</Wix>
This should be a property set automatically in the installer at run time, LogonUser.

Wix Installer Going to Wrong Path on Command Line with Admin Privilege

I built a simple installer in Wix which will place a couple of data files in a specific folder in a preexisting product installation so that the user doesn't need to know anything about the product's installation in order to update their data files. The product stores its installation path in an environment variable (ENVVAR) which I'm using here to calculate the path of its NewData subfolder.
When I double-click the .msi or run it from the command line (msiexec /i filename.msi) it works perfectly and the files show up in C:\ProductPath\NewData. However, if it's installed with elevated privileges (msiexec /a filename.msi) the files go to the root of D:\ (which isn't even the same drive the product is installed on.)
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"
xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension">
<?include InstallVariables.wxi ?>
<Product Id="*"
Name="Product Name"
Manufacturer="My Company"
Version="$(var.Version)"
UpgradeCode="guidgoeshere"
Language="1033">
<Package Description="Description $(var.Version)" Comments="Install package for my product."
InstallerVersion="300" Compressed="yes" InstallScope="perMachine"/>
<Media Id="1" Cabinet="Cabname.cab" EmbedCab="yes" CompressionLevel="high"/>
<SetDirectory Id="PATHMAP" Value="[%ENVVAR]\NewData" Sequence="first" />
<!-- Describe the folder layout here. -->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="PATHMAP" FileSource="..\New Files">
<Component Id="File1" Guid="guidgoeshere">
<RemoveFile Id="Remove_File1File" Name="$(var.File1Pattern)" On="both" />
<File Id="File1File" Name="$(var.File1)" Vital="yes" KeyPath="yes" />
</Component>
<Component Id="File2" Guid="guidgoeshere">
<RemoveFile Id="Remove_File2File" Name="$(var.File2Pattern)" On="both" />
<File Id="File2File" Name="$(var.File2)" Vital="yes" KeyPath="yes" />
</Component>
</Directory>
</Directory>
<Feature Id="FeatureId" Title="New Files for a Feature" Level="1" >
<ComponentRef Id="File1"/>
<ComponentRef Id="File2"/>
</Feature>
</Product>
</Wix>
Note that the file removal in the components is intentional; if there is an existing version of either file (which may have a slightly different file name -- not my choice) I want to remove and replace it. The patterns used to do so are in the include file and are working properly.
The command line: msiexec.exe /a filename.msi will not trigger installation with elevated privileges, but rather an administrative installation. Follow the link for a description - it is important that you do for a complete description. Essentially an administrative installation is just an extraction of embedded files in the MSI to make a network installation image from where people can run a regular installation of the MSI (better explained in the linked answer above) - administrative installations don't install anything at all - it is a mere extraction.
You should be able to control the output directory of the administrative installation by providing a TARGETDIR like this: msiexec.exe /a filename.msi TARGETDIR=C:\MyOutputFolder\. Your MSI is probably lacking a basic GUI to show the administrative installation's dialog sequence - which makes the extraction happen without any parameters specified (hence you output to the largest drive on the box by default). You might want to consider linking a standard dialog set such as <UIRef Id="WixUI_Mondo" /> for your MSI. You can see a step-by-step description of how to do this here: WiX installer msi not installing the Winform app created with Visual Studio 2017. This will give your setup basic, standard GUI for both regular installation and administrative installation. Very useful I think - you should also set your own license agreement - I have updated the linked answer to include that.
I think this is the end of the answer for you. Installing with /a isn't installation with elevated rights - essentially - it is just an extraction of files. But do link in that default GUI to make your MSI more standard and better overall.
A couple of comments on the environment variable approach. I have never stored anything like that in environment variables. I usually just write to my own location in HKLM and read back from there either via a custom action or using MSI's built in search feature (preferably the latter - it is much better to rely on built-in MSI features. I am a little sloppy with read-only custom actions at times, but very much against read-write custom actions. You can see why here: Why is it a good idea to limit the use of custom actions in my WiX / MSI setups? - a digression I guess). WiX can easily define these searches and set the search result to your property: Define Searches Using Variables.
Maybe a quick link to "The WiX toolset's "Remember Property" pattern" by Rob Mensching (WiX creator). This is quite old now, there may be a new, smarter way to do this that I am not aware of yet.
If I were you, I would rather download these updated files from the network rather than deploy them like this into a "data folder" using Windows Installer. Deployment of user data files has always been problematic with MSI with its complex file overwrite rules and "quirks". Though perhaps not entirely related to your use case, here is a description of other approaches you can use to deploy data files for your application - perhaps in a more reliable fashion: Create folder and file on Current user profile, from Admin Profile. Maybe have a quick skim at least.
There are two types of environment variables: user environment variables (set for each user) and system environment variables (set for everyone).
By default, a child process inherits the environment variables of its parent process. Programs started by the command processor inherit the command processor's environment variables.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682653(v=vs.85).aspx
Probably, you can check machine users and if product is installed for all users point to system environment variable in Local Machine.

How to give current user full access control permissions for the installed application Program files folder

I'm new to WiX. Using WiX v3.0.
I want to create one installer for the application.
If I install the application, it is installed in C:\Program Files\Appln\.
I want to give full access control to the Appln folder while installing my application.
Is it possible? I don't want to manually set the full access control permissions for the folder.
First, this isn't a recommended design. The user should not be able to modify things in ProgramFilesFolder for a great many reasons. However, you can do this with the Permission element. It'd look a little like:
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder' Name='PFiles'>
<Directory Id='ApplnFolder' Name='Appln'/>
</Directory>
</Directory>
<Component Id='GrantTooManyPermissionsToApplnFolder' Directory='ApplnFolder'
Guid='PUT-GUID-HERE'>
<CreateFolder>
<Permission GenericAll='yes' User='[UserSID]' />
</CreateFolder>
</CreateFolder>
The important parts are the CreateFolder and Permission elements. CreateFolder defaults to the Directory of the Component and gives a place to hang the Permission element. The Permission element is pretty self-explanatory. The UserSID property is a built-in MSI property.

How do I install to LocalAppData folder?

Following directory setting works perfectly for me.
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id="ProgramFilesFolder">
<Directory Id='INSTALLDIR' Name='MyApp'/>
</Directory>
</Directory>
However, when I tried changing "ProgramFilesFolder" to "LocalAppDataFolder", I got lots of error when using light to link and generate my msi:
D:\runGroup.wxs(53) : error LGHT0204: ICE38: Component cmpA5561BE36D80EB58252E69DDA0C2FF8C installs to user profile. It must use a registry key under HKCU as its KeyPath, not a file.
D:\main.wxs(38) : error LGHT0204 : ICE64: The directory INSTALLDIR is in the user profile but is not listed in the Remove File table.
Looks like "LocalAppDataFolder" is not acceptable for WiX, while I believe it is one of the system folder properties which defined in here.
What am I supposed to use for LocalAppData folder?
I converted an application from being a perMachine install to be a perUser install. In order to properly convert the install I had to add a registry key for each of the components I have.
Originally I had the following:
<Component Id="C.MyExe">
<File Id="Fi.MyExe" Name="$(var.MyExe.TargetFileName)" Source="$(var.MyExe.TargetPath)" DiskId="1">
<Shortcut Id="SC.StartMenu"
Directory="D.ApplicationMenuDir"
Name="$(var.AppName)"
WorkingDirectory="INSTALLDIR"
Icon="MY_ICON.ico"
IconIndex="0"
Advertise="yes"
/>
...
When I moved the exe component to the user install I had to do something like this:
<Directory Id="LocalAppDataFolder" Name="AppData">
<Directory Id="MyAppDirectory" Name="$(var.AppName)">
<Component Id="C.MyExe" Guid="{MY_GUID}">
<CreateFolder />
<RemoveFolder Id="RemoveMyAppDirectory" On="uninstall" />
<RegistryKey Root="HKCU" Key="Software\MyCompany\MyApp">
<RegistryValue Name="MainExe" Value="1" KeyPath="yes" Type="integer" />
</RegistryKey>
<File Id="Fi.MyExe" Name="$(var.MyExe.TargetFileName)"
Source="$(var.MyExe.TargetPath)" DiskId="1" Checksum="yes">
</File>
</Component>
...
The most important part is that you will have to add a registry key which points to HKEY_CURRENT_USER. I added a registry value for each component which indicates that the component is installed.
I also had to remove the following: Advertise="yes".
I had this problem recently. I wanted to convert my installer from per-machine to a per-user but was getting ICE38. I asked on wix-users and one opinion was that you can ignore ICE38 because that was meant as a check for per-machine installs.
See the discussion at wix-users.
Since that is the case, ICE38 is (in my opinion) incorrect and you will want to ignore it. ICE38 implies you are installing per-user resources in the context of a per-machine installation but never verifies that this is so.
Actually authoring a per-user install requires that you ignore ICE38
because it won't ever be accurate for that world.
[Edit]
Looks like you got help here.
From Peter Shirtcliffe:
This is my own, admittedly inexpert, understanding of per-user installations:
Installing to subdirectory of LocalAppDataFolder is perfectly OK in a
per-user MSI. Because of certain scenarios relating to roaming users, you
need to add components containing elements for any
directories you create under LocalAppDataFolder. That's why ICE64 is
appearing.
The ICE38 error is slightly misleading: since you have a per-user
installation, it's safe to ignore as long as the user cannot pick an
alternative installation location that is common to all users. ICE38 is
checking for the situation where multiple users all install the same
component to the same path.
Just posting to help other people (like me).
Ok, just found that we can do it by overwriting "ProgramFilesFolder":
<SetProperty Id="ProgramFilesFolder" Value="[LocalAppDataFolder]" Before="CostFinalize"><![CDATA[NOT Privileged]]></SetProperty>
Another thing to do is, in <Package> we need to set InstallPrivileges to limited.
Well, I can see no reason why "ProgramFilesFolder" can be used directly while "LocalAppDataFolder" can't.
Are you installing per-user or per-machine? Also, what OS versions are you targetting? You might want to read:
Authoring a single package for Per-User or Per-Machine Installation context in Windows 7