Friday, March 27, 2015

Powershell execution policy

I only run Poweshell scripts a few times a year when they're needed for some particular task, such as clearing all Event Logs at once (which is handy). I always run the scripts inside the UI (PowerShell_ISE.exe) because whenever I run a script from a command prompt I get

Name cannot be loaded because running scripts is disabled on this system

I finally got so sick of this irritation that I had to find a way around it. The reason and answer finally appeared on TechNet About Execution Policies. More explanation can be found on Scott Sutherland's Page and Carlos Perez's Blog. It turns out that the default script execution policy is "Undefined" which equates to "Restricted", meaning you simply can't run scripts.

Imagine that ... it's so secure you can't run it. It's like making your car theft-proof by welding the doors and windows shut. Surely it would be a better default to use "RemoteSigned" as the default policy and to let standard operating system security oversee what a script can do. Anyway, do this from an elevated command prompt:

powershell Set-ExecutionPolicy -ExecutionPolicy RemoteSigned

This sets the policy for the local machine. Thereafter you can run scripts, subject to a more sensible policy.

Monday, March 16, 2015

Custom Actions and Uninstall

This is just a reminder and warning about the rare but infuriating problem where the uninstall events in a Visual Studio Setup Project custom action (CA) will not run. While experimenting with a simple Setup Project to test some CA code I was changing various project properties and various haphazard ways and installing and uninstalling over and over many times. At some point, the CA's uninstall events and method overrides stopped running. Not matter what properties I changed, I could not get the uninstall events to ever run again. I searched the web and found dozens of questions from people who had suffered a similar problem. All of the advice I found was either irrelevant or made no difference.

I remember this problem happened to me many years ago, and I think after hours of suffering I gave up and either restored the original project or recreated it.

This time, as a stubborn challenge, I spent hours trying to get the project working correctly again, but msiexec verbose logs revealed nothing useful and comparisons of the vdproj files was not possible because their structure changes unpredictably each time it's altered and saved. I simply could not get the CA uninstall events to run again and I could not figure out what had corrupted them.

I created a brand new Setup Project, set the properties and custom action code back the way they were in the original project, and it all worked perfectly.

Overall, I guess the lesson is to be careful with Setup Projects, back them up and remember what you change.

I have a love-hate relationship with Setup Projects. Once you get a feel for them and find where they've hidden the various common settings you need, a familiar friendly MSI installer can be created very easily. However, as soon as you find you are pushing them too hard, then it's time to switch over to something like WiX and be prepared to wade into a morass of XML. To paraphrase the old adage: WiX gives you complete control, but you have to control everything.

16-Mar-2015

P.S. Setup Project (*.vdproj file) support was withdraw with the release of Visual Studio 2010, but after sustained protest they were made available again in 2013 as an add-in.

Saturday, March 14, 2015

Registry secrets and permissions

I tried to convince a product manager that we must not store user passwords in the application's SQL Server database in any reversible form. Plain text is a suicidal risk, and encrypted passwords lead you into the murky world of dealing with secret keys that encrypt the passwords and trusting who has access to those keys. I believe that it is highly objectionable to store passwords in any reversible form, as you are then at risk from sloppy security implementations or corrupt people. What if you use the same valuable password for a magazine subscription and your Internet banking? What if your password is the name of your secret lover? (don’t laugh, this has happened). You don’t want your passwords revealed to anyone by any means.

It is preferable to store passwords as a one-way hash. In .NET you can use the Rfc2898DeriveBytes class. I like to assign a permanent and immutable Guid to users which can conveniently provide 16 bytes of salt for the hash.

For historical reasons I was forced to reversibly encrypt user passwords, so I had to play the game called ‘hide the secret’. This tricky issue is discussed in Chapter 70 of Keith Brown’s book The .NET Developer’s Guide to Windows Security, and in Chapter 9 of Michael Howard’s Writing Secure Code.

I eventually decided that C# code in a CA (Custom Action) in the Visual Studio 2015 Setup Project would put the secret in a registry key under HKLM and set ACLs on the key to allow read access only by the specific account that needed it, which in this case was NETWORK SERVICE.

I had never written code to set registry permission before, so I wrote a small test program to get the C# code correct before migrating it into the installer. It took a bit of fiddling around to get the desired access rule combinations, so I have made the test program public as a reminder to myself and possible help for anyone else who wants to do this sort of thing. See:

https://dev.azure.com/orthogonal/RegKeySecurity

You must run this test program as an Administrator. Then run regedit as an Administrator and you will see the keys created under HKLM and the child key containing the secret will have special non-inherited permissions. Run regedit as a normal user and you will be denied access to the child key.

32/64-Bit Issues

The first time I migrated my test code into the CA, installed the application and ran it, I received a null argument crash. I discovered that the CA had created the registry keys under HKLM\Wow6432Node, but the application was reading the keys from HKLM\SOFTWARE.

So the installer was writing to the 32-bit view of the registry but the application was reading from the 64-bit view of the registry. Changing the TargetPlatform property of the Setup Project from x32 to x64 has no effect on the installer's registry view.

Since I know that all installations will be on 64-bit Windows servers I can force the CA to write to the 64-bit view of the registry by replacing this:

var machinekey = Registry.LocalMachine

with

var machinekey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)

Then at runtime the 'Any CPU' application running on 64-bit Windows will be able to read the secret data from the registry.