Set directory permissions with inheritance during application installation? - windows

I found a thread on the Microsoft Forums where the answer tells how to set directory permissions using the LockPermission table. I tried it, but it doesn't seem to set the inheritance for any of the subdirectories. I need to be able to set the permissions for a particular folder that I create and have those permissions be inherited by all of the files and directories within and beneath it. Is there a way to do this without having to add a line in the LockPermission table for each and every directory (and file) that I want to affect?

For anyone looking to know the joys and pains of using MsiLockPermissionsEx, here is a tutorial, some best practices and a helper script. The helper script extracts SDDL from existing system resources - so you just use Regedit and Windows Explorer to set permissions and the helper script extracts them for you.
The article also discusses the challenge of supporting XP and Windows 7 permissions with a single package.
You can check it out here: http://csi-windows.com/toolkit/csigetsddlfromobject

You can either see if the MsiLockPermissionsEx support in MSI 5 handles this (and is an acceptable dependency for you as currently it's only available on Windows 7), or you can implement custom support. The LockPermissions support available in earlier versions of Windows Installer has the limitation you describe (and others).

Related

How to detect OneDrive online-only files

Starting from Windows 10 Fall Creators Update (version 16299.15) and OneDrive build 17.3.7064.1005 the On-Demand Files are available for users (https://support.office.com/en-us/article/learn-about-onedrive-files-on-demand-0e6860d3-d9f3-4971-b321-7092438fb38e)
Any OneDrive file now can have one of the following type: online-only, locally available, and always available.
Using WinAPI how can I know that the file (e.g. "C:\Users\Username\OneDrive\Getting started with OneDrive.pdf") is online-only file?
After years, I'm still using FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS attribute described here to determine if a file or a directory is completely present locally or not.
Microsoft docs says the following for FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS:
When this attribute is set, it means that the file or directory is not fully present locally. For a file that means that not all of its data is on local storage (e.g. it may be sparse with some data still in remote storage). For a directory it means that some of the directory contents are being virtualized from another location. Reading the file / enumerating the directory will be more expensive than normal, e.g. it will cause at least some of the file/directory content to be fetched from a remote store. Only kernel-mode callers can set this bit.
There are some advantages of FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS:
It can be used for both files and directories.
It can be set in kernel mode only, so there is no chance for anyone to set the attribute arbitrary.
And as it described in this answer, there are still some interesting undocumented attributes which can provide additional information about cloud files.
Note: I didn't accept Jonathan Potter's answer because I mentioned FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS attribute in comments and started using it a year earlier than he updated his answer.
To check for "online only" all you need is to call GetFileAttributes() and see if the FILE_ATTRIBUTE_OFFLINE attribute is set.
In fact this isn't new for OneDrive, that attribute has existed for a long time.
There are other OneDrive attributes available via the shell (although the property you need is PKEY_StorageProviderState rather than PKEY_FilePlaceholderStatus) but "online only" is easy to check for.
Edit: Another filesystem attribute, FILE_ATTRIBUTE_PINNED is new for Windows 10, and is used by OneDrive to indicate a file that's "always available".
Edit: As of 2019 it appears that OneDrive now uses FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS rather than FILE_ATTRIBUTE_OFFLINE, as suggested below.
Edit: PKEY_StorageProviderState was broken in Windows 10 1903, and still not fixed in 1909. It returns 4 ("uploading") for all files in any apps other than Explorer.
Take a look at the PKEY_FilePlaceholderStatus property for the file (at the shell level, not the file-system level). This blog post has a example program you can test. This question also hints to some undocumented properties you might want to take a look at.
Microsoft has a UWP example on MSDN.

Writing config file in C:\Program Files (x86)\MyApp\myapp.cfg, vs. Administrator privilege

I have made an installer (using Inno Setup) for my software, which is finally installed in
C:\Program Files (x86)\MyApp\myapp.exe
as usual on Windows platform, and it works perfectly.
A problem comes when my software tries to write its config file here :
C:\Program Files (x86)\MyApp\myapp.cfg
here a Permission denied error arrives !
The only solution I have found is "Run software as administrator", but I don't like this solution : I don't want to ask the final user to have to do "Run as adminstrator" each time they will use the software.
Is there a clean solution for having the right to write in C:\Program Files (x86)\MyApp\myapp.cfg without asking the final user to "run as administrator" ?
As #TLama said, never ever try to write to the application folder. That was never the recommended practice and since Vista Microsoft has disabled it (by first allowing you to write but actually redirecting the writing to another directory, a functionality which in later OS versions seems to have been removed - and good thing too as it was making support hellish).
To retrieve a known folder path, use SHGetFolderPath function. Depending on whether the application data is shared among all users or just used by the user running the app, you should call SHGetFolderPath either with CSIDL_COMMON_APPDATA or with CSIDL_LOCAL_APPDATA respectively. Avoid writing directly in those folders but create subfolders like <your company>\<your app> and use those instead.
Note that SHGetFolderPath has been deprecated and SHGetKnownFolderPath should be used in lieu of it but only if you don't need compatibility with Windows versions prior to Vista.
For completeness sake, this thread also describes how to import SHGetFolderPath into Python.
Update: Corrected to avoid using environment variables and SHGetFolderPath instead per comments by #RemyLebeau #TLama

How to make MSI file by vs2010 that allow my app to create .txt file in Client Machine which is installed in C:\Program File\myAppFolder

I'm deploying a .NET application with VS2010. My application creates .txt file in the logs folder in the same directory with .exe:
app.exe
add.exe.config
logs (folder)
I used setup project to create a MSI installer. When I installed in the client machine C: drive or any drives I have no problem to create the .txt file, but when I installed in C:\Program File\myAppFolder or C:\Program File(x86)\myAppFolder I cannot to create the .txt file.
It is a poor design to write to install location for your application. It is better to write to the ApplicationDataFolder. The ApplicationDataFolder is under the user profile and the application will have access to write there when run as that user. #Ken White provided a very good pointer to an existing StackOverflow answer about this.
If this is a legacy application that must write to that folder, then you'll need to modify the permissions on the log folder such that all users can write to the folder. This is possible to do with the Windows Installer (aka: MSI) but I'm not sure that the Visual Studio setup projects expose it. The WiX toolset definitely supports doing such things.
An old post but I needed to do similar recently so I guess it is still valid! While I don't advocate bad design, in the real world sometimes we have to bend to requirement.
Writing to the application folder is possibly under Win7 and it is possible to set this up via an installer class in an MSI created by VS2010. You just need to give a relevant group (suggest either the "Users" group, or if you want to give more control over who gets what, supply a selection screen) Write-Data access.
Using DirectoryInfo on a path you can then get the security data from GetAccessControl.
When you have your Group known, get the SID for the group and AddAccessRule also supplying the required ControlType value.
Then set the access control on the DirectoryInfo object (SetAccessControl) using the security data object.
You can get the SID from the Groups principal object if you do a search with PrincipalSearcher.
Hope this helps
paul
This generally all depends on:
Whether your app requires to be run as administrator for other reasons and..
Whether your app is provided for limited users.
If the app requires elevation for a bunch of other reasons (and not just updating files in restricted locations) then the normal way is to have an elevation manifest embedded in your app. This isn't a good thing from the security point of view, but if you absolutely need admin privilege then this is the way to do it.
If the only operation requiring elevation is updating/creating data in the Program Files folder then don't put the file there. Every case of this that I've seen has been lazy programming where the code just refers to the file name and consequently it goes in the Program Files folder (more accurately in the same folder that the app runs in). The cure for this is to put the data file in the correct location (such as User's Application Data folder). As Rob Mensching says, you should alter the permissions on the install folder only if this is a legacy app that you cannot change.

How to make WIX create files to Program Files folder in the installation? I have "Access defined"

I am creating a WIX installer project. During one managed customized action, I need to create a file (other than the deployed files specified in the components of WIX) in the installation folder, which by default is the Program Files folder. I am experiencing the "Access denied" problem in Windows 7. After some searching, I found out that people say it is not advisable to create files into Program Files folder. Instead, try to create files into for example AppData folder. For example, see this link:
C# Access denied to path in a Windows Application
But my question is, the generated file is crucial to our SW, so it must reside in the installation folder. Isn't it the target of SW installation, I mean, to create file in most of the cases Program Files folder? Does it mean the only files should be added into installation folder, during the installation, are the deployed files (basically the targets of XCopy)?
My file can't be made deploy-able in the WIX, i.e, it can't be made ready before the installation. So what's the proper way or best practice to handle such situation: a file must be generated during the installation, into the installation folder. It is not some log file that I can put somewhere else. I tried to create a Permission element in WIX for the INSTALLADIR, although it seems to be against the rule mentioned in the link, but it still failed. Thanks!
UPDATE:
Based one MichaelUrman's commen, some more information. The generated file is needed after the SW is installed and necessary during normal launch of the SW. And I think it needs to be modified during normal use after the installation. And as I mentioned my a comment to #caveman_dick answer, my CA is actually in commit phase, I don't know whether there is any difference between it and normal deferred CA
Set the custom action to Execute="deferred", that will run the command elevated and should give it the required permissions to create the file.
Since you need to update that file from the main application, and I'm assuming your application does not require elevated privileges, you have three options.
The first is the worst: without a manifest, your executable's attempts to write to the Program Files folder will typically result in it being redirected to the Virtual Store (see File Virtualization). It sounds like this isn't happening in your case, so you can't use it.
The second option is to modify the application to store this in an appropriate location such as the ProgramData folder, or Common Documents, or (if appropriate) a per-user location under LocalAppData. This is typically the best approach, but has the highest development costs.
Finally the third option is to create the file and change its permissions (or in some cases to change the permissions on the folder containing the file), allowing limited users to modify this file. See LockPermissions or MsiLockPermissionsEx for the Windows Installer way to approach this. Change the permissions on as few files or folders, as restricted as possible, to keep the system as safe as possible if you go with this option.

How do I set file permissions for non-admin users in VB6?

I have an old update program written in vb6, which runs as admin. However, because it runs as admin, all the files it downloads and saves are read-only to other users. Even files in public places like the shared application data folder (which is where I'm saving the files in question).
I'm lucky I found this before the 'vista-compatible' release. Vista hides the problem by redirecting non-admin writes and future reads to a sortof 'virtual' folder. But the next update may replace the file, and the non-admin program will still go to the virtual folder and use the old file.
As the admin user, how do I allow other users full control of files I write in vb6?
The way I do this is to make it an Installer responsibility.
Use VSI 1.1 to create an Installer MSI for your application. Create an application data folder under CommonAppDataFolder.
As a post-build step run a script to perform the following:
Set the MSI database for a per-machine installation: Property table, row with ALLUSERS set to 1.
In the Directory table, locate the entry for CommonAppDataFolder and obtain its directory Index. Use this Index to query the Directory table for an entry where CommonAppDataFolder is the parent and obtain its Index (this is your app data subfolder).
Look in the File table to obtain the component Index of your program.
Create the CreateFolder table in the database if it isn't present. Add a row to CreateFolder for the desired application subdirectory by its Index, tying it to your program's component Index.
Create the LockPermissions table if it isn't present. Insert a new LockPermissions row for your application data subdirectory, giving it FILE_ALL_ACCESS for Everyone.
That's about it.
You can do it this way, or use VSI 1.1 and then edit the MSI using Orca, or probably by using a 3rd party MSI authoring tool these entries will be settable via its GUI and can be saved in the Installer project. I just use a small WSH script I run after each VSI 1.1 build.
AFAIK this is the recommended way of accomplishing such things according to Windows application guidelines. If your needs are fancier you might use multiple subdirectories or sub-subdirectories some allowing full access, some read-only, etc.
Your program can locate the folder using Shell Automation objects or by calling Shell32 as a standard DLL (using Declare Function or a TLB).
It's not necessarily who writes the file, but where they write it to. The program files folder and it's sub folders are read-only to all standard users by default. Try using the All Users Application Data folder instead.
This is a little tricky for VB6, since it was not at all designed with Vista in mind. Some of the relevant folders were re-named and there's no way I know to get vb6 to give you the exact folder you want short of using a "Declare Function" alias to call directly into the windows API.
So the easiest reliable way I know to find a suitable location is to use the %ALLUSERSPROFILE% environment variable. That returns "C:\Documents and Settings\All Users" by default on XP and "C:\ProgramData" by default on Vista. From there you can look for an "Application Data" folder. It won't be there and you don't need it on Vista, but creating one if it doesn't exist won't hurt anything. That gives you a consistent folder structure on both systems from which you can create a sub folder for your app to use as a work space.
And one final note: this isn't a new change for Vista. Program Files folders have always been read-only to standard users by default. XP worked the same way. It's just that so many people run as administrators in XP you might be able to get away with it.

Resources