You can jump straight to the technical answer below, but I have a few things I want to say about ACLs first :-).
Shortest possible answer: Essentially this is probably just a "deny ACL entry" overriding the "read grant ACL entry". Rather, the "write grant ACL entry" should be removed, and no deny entries should be added. ACL inheritance must probably be disabled.
With that written, do consider what may be wrong with your deployment design when you need strange ACL permissioning. More details below.
The Murky World of ACLs
Once upon a time... Just kidding. Feels strange to write this "sermon", but I wrote an ACL permissioning component for MSI use in C++ once - it was quite hairy to do and I recommend staying away from permissioning if at all possible. It is not impossible to use or work with, but it is very error prone. One complication is the amount of "inheritance" going on with ACLs and permissioning. I made some mad "posters" to deal with these ACL / DACL issues back in the day. I wish I still had them. There were some issues with deny rights taking precedence over grant rights as I recall. There were many gotchas.
So applying custom permissions like this is a great way to trigger MSI runtime errors that are very hard to deal with (and maybe application errors too) - just want to mention that. ACL permissioning is notoriously easy to mess up, and you need to test upgrade, patching and uninstall scenarios thoroughly running as a regular user (managed environments with elevated MSI install rights), running as a real admin user or uninstall triggered remotely (running as a system user) or you could be in for very nasty surprises.
I am sure the WiX guys have added protection against the worst pitfalls, but I would still avoid ACL permissioning - it is an anti-pattern for deployment indicating something wrong with the application design in most cases (in my experience in all cases, let's say almost all). I have written about deployment anti-pattern in a rather messy summary here, see section 14: How do I avoid common design flaws in my WiX / MSI deployment solution?
In light of that, I just want to ask some questions (will try to follow-up when I have a better understanding of your scenario):
- Could you retrieve these settings from a database on application launch and be better off? (client/server app). You get centralized settings management and good ability to change the configuration whenever needed. You can even have different settings for different groups of users if you define "roles". No pesky files to deal with. I prefer this for all user data - subjective note. Avoids all roaming profiles and storage issues that mess with files. Network connectivity is an issue though. You may still need a cache file... Or the app just doesn't work without network connectivity - which is normal for client/server anyway I suppose.
- Be careful with network access requirements for general distribution third party apps. You could run into proxy issues with apps having no generalized network access except via the proxy (which you must detect of course).
- If you want no changes to the file at all, could you make these settings "internal defaults" in the application instead? Just settings specified in the source code rather than a configuration file? The presence of a config file indicates a need to configure something externally - does this need really exist? (I assume it does, just checking). You can always rebuild your binary to change settings and redeploy? This might work for an in-house corporate application. Not great though. You could set a flag in the registry and allow the config file to override the defaults if present on disk when the flag is set?
- Where do you install this file? If you install under ProgramFilesFolder somewhere - In the normal fashion - then regular users won't have write access to the file in the first place. Administrators might, if they run with elevated rights. I am not sure how critical it is that the file isn't written to. It must be crucial? So you want to deny administrators write access?
- Can you mask the file so it looks like something else? Maybe encrypt it to obfuscate the config data? Can it be switched to a binary format that you can deploy a hotfix to without rebuilding your whole application? Maybe just encrypt the actual XML file value that you want to protect? Or use a binary file that can't be tampered with because the source knows its hash?
So in summary, install to a location that is naturally write protected, remove the config file and read from a database instead (where you have full control and instant deployment to all when the network is up), obfuscate the content of the file or make it binary, eliminate the file and make the settings hard coded in the source code. This may all be mad for your scenario - I am aware of that, but please run through the options for sanity. Chose complexity you can deal with, rather than too many unknowns and risky features?
Technical Answer (attempted)
Now for the actual technical question. Can you show us the property screen with the ACL information for the file in question? Right click file => Properties => Security
. What does it say? Here is a sample dialog, in Norwegian but you get the picture. We need to know in detail what this dialog shows. In particular we need to know if there are any deny entries (the rightmost column below - "Avslå" in norwegian meaning "Deny"):
I would toy with this dialog and change the settings until it behaves like you expect. Removing all deny rights first of all, and then working to determine how inherited rights affect the actual security status of the file or object. Use right click and "Run As..." to run Notepad.exe or some other text viewer in the testing context you need - another user (which you already seem to be doing).
The Advanced button will take you to a dialog where you can mess with the "Inheritance" setting for the ACL. In other words if the ACL for the parent object applies to the object you have selected to view or not.
You must in no case whatsoever apply any deny settings to LOCALSYSTEM or SYSTEM or whatever the account is called. You will have yourself an uninstallable package.
GUESS: I think it is just a matter of disabling write access for administrators and users without adding any deny flags and making sure they still have read access. So no deny entires, and no write access flags. You probably have to turn off inherited permissions as well if you really want administrators to have no write access, but this I would have to test out to be sure.