Tuesday, July 08, 2008

I made reference a few times last year to some big things being in the works but that it was hush hush.

Our company, called CarSpot.com since 1995, was acquired by AutoTrader.com, the biggest player in the online automotive space with an overwhelming market share.  CarSpot was picked up for our innovative online, mobile, and desktop solutions to pervasive data aquisition, aggregation, and distribution problems.  To AutoTrader, having a small satellite office that is much more agile than a multi-billion dollar entity was an attractive proposition.  Let's face it, some large companies couldn't change the font of a paragraph burried in an obscure section of their website without a 50 person team of project managers, business analysts, marketing, managers, designers, and developers.  Here at CarSpot, though, an impressive amount of functionality was written and supported by basically four technical people.

CarSpot traditionally was very casual.  Developers have always been trusted to get their work done, a simple practice that's practically unheard of out there.  People came and went as they pleased, a lot of beer was purchased on the company credit cards.  There was no internet monitoring, we played a game of Quake III at work sometimes, almost any link you might get from a friend marked "NSFW" was really just fine at CarSpot.  We had power and freedom and we did some good work; we were encouraged to goof off on things that might turn into good things for the company.  What we didn't have was tons of money to buy all the servers we should have had for redundancy and there were sometimes some tools it obviously made sense to have that we just couldn't buy.  I had been consulting with CarSpot for years, but when I joined full time in August of 2006 I was the only spouse/kids/mortgage employee in the shop; benefits weren't all that family friendly.

In the post-purchase world, the former owner is still the President of this division.  We have all the upside of a large company like HR, benefits, money, and infrastructure, and AutoTrader is making sure we keep our culture.  We are considered the "R&D Division" and are expected to keep AutoTrader.com ahead of the technology adoption curve.  With a mix of more traditional projects and products with lists of enhancements for sales and "mess with this to see what works and what could be better" type mandates this is shaping up to be an ideal place for geeks to work.  Hopefully the two employees I've added to my group this year are enjoying themselves. 

For the longest time, we were forbidden from publicly mentioning this pending an "Official corporate communication strategy".  This never happened, so its high time I made a public announcement to the geek community.

Tuesday, July 08, 2008 11:39:16 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Tuesday, March 18, 2008

Securing ClickOnce Deployments

 

I have written in the past (http://www.damonpayne.com/PermaLink,guid,da078e56-eb5b-4616-8605-13c38d54efc3.aspx) about some of the plumbing involved in ClickOnce and Client Application Services geared towards preventing unauthorized operation of a client application.  At the time I noted that there is at least one issue with this level of “security”.  While it does keep unauthorized users from running your application, it doesn’t protect your code.  As has been lamented elsewhere, a ClickOnce application deployed on the Internet does not have any supported means of keeping people from getting your code.  Even with obfuscation this may be undesirable: we don’t want people getting a hold of the application manifest file and subsequently the assemblies.

A while back I started down a few paths to secure the code and the Application Deployment Manifest.  I have a pretty decent partial solution for protecting your code.  Read on.

Step 1: Security by Obscurity?

Your ClickOnce deployment consists of a deployment manifest, the executable, and all of the referenced assemblies and content files required to run your Windows Forms or WPF application.  By turning off directory browsing and not having any publicly available links to your .application file you can discourage casual spelunking.   I’m not very satisfied with this as the only aspect of a code protection solution. 

It’s fairly unlikely than an uneducated attacker (outside job) could guess the names of the assemblies that make up your application.  If only you could lock down the .application file…

Eliminating the Obvious Suspects

To get the obvious thoughts out of the way:

1)      You cannot protect a ClickOnce application deployment manifest using Forms authentication.  The client side aspects of ClickOnce that get installed with .NET 2.0 can’t handle Forms authentication.

2)      Windows Authentication is at best a poor option here, and not a real option for most people.  The performance of “Remember me” in an NTLM challenge form is flaky and is not supported in the Internet zone.

3)      ClickOnce does not support HTTP Basic authentication schemes.

An Idea

One nice thing about ClickOnce is that, like Silverlight, it’s really a Client Side technology.  You don’t NEED to be running Windows Server and IIS in order to benefit from these technologies.  However since I do know some Microsoft server side technologies I had an idea.

I wanted to associate a Product Key of some kind with a ClickOnce deployment.  Product Keys are not the final word on application licensing but it is a means of accountability.  Look at Windows Genuine Advantage: you can’t keep people from sharing CD keys but if you see the same CD key being used 1,000 times from different hosts all over the nation, you can shut it down.  Given a test deploy URL:

http://localhost:5785/CarSpot/MyApp.application

It would be nice to be able to do something like this:

http://localhost:5785/CarSpot/MyApp.application?ProductKey=ZZZZ-ZZZZ-ZZZZ-ZZZZ

… and have it be meaningful.

HttpHandler Attempt 1

To keep people from getting at my .application file, I thought a simple IHttpHandler implementation configured to intercept requests for the .application extension could be the answer. 

Web.config and sample code:

<add verb="GET,HEAD" path="*.application" type="ClickOnceHandler"/>

    public void ProcessRequest(HttpContext context)

    {

        string key = context.Request.QueryString["key"];

        if (ValidateKey(key))

        {

            //Go ahead and give up the .application file

        }

        else

        {

            throw new ApplicationException("Invalid or missing product Key");

        }

    }

I had a notion of allowing users to sign up for the application online by entering a privately exchanged product key in a web page:

The code behind for the install button is simple:

Response.Redirect("Publish/MyApp.application?key=" + _prodKeyTxt.Text);

Sadly, the solution is not this simple.  The Client side ClickOnce libraries exhibit some bizarre behavior.  Using Fiddler and the debugger, this is what I see:

1)      The .application file is requested and the HttpHandler validates the product key.

2)      Mysteriously, ClickOnce requests the entire URL of the .application file again: http://localhost:5785/CarSpot/Publish/MyApp.application?key=123; The HttpHandler validates the key and writes the contents of the .application file to the Response.

3)      Mysteriously (and infuriatingly) ClickOnce requests the .application file again: http://localhost:5785/CarSpot/Publish/MyApp.application ; note the missing piece of the query string.

I was unable to dig up any reason for what ClickOnce is doing here, but I do know that without getting that product key my scheme is ruined.  Looking at the .application file there’s a likely explanation:

<deploymentProvider codebase="http://127.0.0.1:5785/CarSpot /Publish/MyApp.application" />

This is where I left it for a long time, while dealing with some other things at work.

HttpHandler Attempt 2

The next, step, I felt, was to dynamically alter the application deployment file so that the provider codebase as recorded by the client included the product key on the URL.  I added some code to do this:

                XmlDocument doc = new XmlDocument();

                doc.Load(path);

                //Get around the issue of the query strong not being

                doc.DocumentElement["deployment"]["deploymentProvider"].Attributes["codebase"].Value += "?key=123";

However, this introduces another issue.  When you choose to give your ClickOnce application Full Permissions (Otherwise why not go the XBAP route?) you will find that you are signing it.   Look what Visual Studio did for me:

<publisherIdentity name="CN=CORNZOR\Damon" issuerKeyHash="" /><Signature Id="StrongNameSignature" {nothing to see here}</Signature></asmv1:assembly>

Yes, my laptop is named cornzor.  At any rate, this signature is there for your clients’ protection: if a man in the middle alters the XML payload of the .application file it will not verify and ClickOnce will refuse to do anything further.  Since I’m not a man in the middle I assumed there must be a way to dynamically do what I want.

http://msdn2.microsoft.com/en-us/library/acz3y3te.aspx

http://msdn2.microsoft.com/en-us/library/bb384246.aspx

I started out with the two links above, researching how the manifests are generated, and initially experimenting with manually generating un-signed deployment manifests from the command line.  After an infuriating XML character encoding setback, these articles did lead me to the answer.  Now I can protect my .application file in an appropriate fashion.

HttpHandler Complete

The articles above give us the tools we need to put the product key in the application manifest for each client and re-sign on the fly.

    /// <summary>

    /// Execute whatever business logic will make this a valid program key

    /// </summary>

    /// <param name="key"></param>

    /// <returns></returns>

    protected bool ValidateKey(string key)

    {

        return key.Equals("123");

    }

    /// <summary>

    /// Create a new signature in the deployment manifest

    /// </summary>

    /// <param name="path"></param>

    /// <param name="pfxPath"></param>

    protected void ReSign(string path, string pfxPath)

    {

        string mageArgFormat = "-Sign {0} -CertFile {1} -ToFile {2}";

        string mageExe = @"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\mage.exe";

        string mageArgs = string.Format(mageArgFormat, path, pfxPath, path);

        ProcessStartInfo info = new ProcessStartInfo();

        info.Arguments = mageArgs;

        info.FileName = mageExe;

        info.UseShellExecute = false;

        info.RedirectStandardError = true;

        info.RedirectStandardOutput = true;

        Process p = Process.Start(info);

        p.WaitForExit();

        using (StreamReader stdOutReader = p.StandardOutput)

        {

            string stdOut = stdOutReader.ReadToEnd();

            //Error handling here

        }

 

        using (StreamReader stdErrReader = p.StandardError)

        {

            string stdErr = stdErrReader.ReadToEnd();

            //Error handling here

        }

    }

This method uses mage.exe to re-sign the application manifest after we’ve modified it using the BuildClientApplicationFile method.  There are two things to note about this method.  You will see that I’m referring to mage.exe from the Visual Studio directory here.  Obviously, installing Visual Studio on servers is not ideal.  Mage.exe also ships separately with the SDKs as indicated in the two articles I’ve linked, which is a more palatable install than Visual Studio.  I have not yet tested to see if mage.exe will properly operate on Windows Server 2003 by itself.

The second note on this method is that we are passing in a fully qualified path to a .pfx key file.  I’ve only tested this using the same .PFX key I used when I initially published the application.

    private static void BuildClientApplicationFile(string path, string signedPath, string key)

    {

        XmlDocument doc = new XmlDocument();

        doc.Load(path);

        //Get around the issue of the query string not being persisted by the client

        doc.DocumentElement["deployment"]["deploymentProvider"].Attributes["codebase"].Value += "?key=" +key;

        doc.Save(signedPath);

    }

This method stores a special version of the application deployment manifest for this particular client.  I didn’t keep this in memory due to ClickOnce’s odd habit of re-requesting the manifest many times.  This means I’ve got one copy of the manifest per client sitting on my server.  In my testing I’ve found two side-effects for publishing updates using this method:

1)      If I re-activate from the URL and updates have been published, the client does not get prompted

2)      If I run the program from the start menu, the client does get prompted for updates.

Here is the main HttpHandler method:

    public void ProcessRequest(HttpContext context)

    {

        string key = context.Request.QueryString["key"];

        if (ValidateKey(key))

        {

            //Just in case the client doesn't know what to do with .application

            context.Response.ContentType = "application/x-ms-application";

 

            //The current version deploy URL

            string path = context.Server.MapPath("~/Publish/CarSpot.MyApp.application");

 

            //Check for existing signed manifest for this user:

            //keeping a specially signed copy of the manifest per client

            string signedFileName = key + ".CarSpot.MyApp.application";

            string signedFileServerPath = "~/Publish/" + signedFileName;

            string signedPath = context.Server.MapPath(signedFileServerPath);

 

            if (!File.Exists(signedPath))

            {

                //Load the generated deployment manifest as an XML document to make it easier

                //to edit.

                BuildClientApplicationFile(path, signedPath,key);

 

                string pfx = context.Server.MapPath("~/Publish/MyGeneratedKey.pfx");

                ReSign(signedPath, pfx);

                //Redirect to original path now that we've created a signed one...

                string redirUrl = context.Request.Url.ToString();

                context.Response.Redirect(redirUrl);

            }

            else

            {

                context.Response.WriteFile(signedPath);

                context.Response.Flush();

            }

        }

        else

        {

            context.Server.Transfer("~/Error.aspx?errormessage=Missing or invalid Application key");

        }

    }

I feel confident this is safe, look at the appref-ms file generated from this install and put in the start menu on the client:

http://127.0.0.1:5785/CarSpot /Publish/MyApp.application?key=123#CarSpot.MyApp.application, Culture=neutral, PublicKeyToken=blahblah, processorArchitecture=msil

The key is part of the URL, so it should be passed to the server every time.

Further reading

Now I’ve locked down my deployment manifest, at least you have to have a valid product key to get access to it.  This is a large step towards completely locking down my application.  Since this is ASP.Net code that I control, I can log the remote hosts that use each product key and disable one if it appears to be too promiscuous. 

What about your other files?  If someone somehow figures out that there’s a file called CarSpot.Snark.Zing.dll in the ~/ClickOnce directory?  I have some ideas for this too, along the same lines as what we’ve done here.  You will note that one of the Publish options for ClickOnce is to “Use .deploy file extension”.  Sounds like an HttpModule could be created to handle the .deploy extension.

Microsoft’s behavior regarding ClickOnce baffles me, but here we have a means of securing your deployments.

Tuesday, March 18, 2008 10:05:56 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Tuesday, February 26, 2008

Invoking a .NET program on Device Insert

Given the popularity of the .NET Platform it seems odd that Microsoft does not provide easier means of doing certain incredibly useful and easy to imagine tasks from the .NET framework.  It took what seems like a ridiculous amount of research to accomplish a simple task:

Lots of programs come up as ‘launch’ options when I insert a storage card/camera/video camera/PDA/Mp3Player/younameit device into a USB port.  I’d like my .NET program to be in that list and to run if the user selects it.

File Types & WIA

The most useful thing you get in Visual Studio towards this goal is file types.  You can build an MSI for your desktop application and make your “.damon” files open with this program and have right clickàOpen appear in the shell context menu for “.damon” files.  I’ve written before about how to do multiple-select and some useless and goofy things with this scenario, but this doesn’t get us anywhere near where we want to be. 

WIA is very useful for media-specific actions like this, and handles the “something was plugged in” scenario in specific cases.  There is a WIA Automation Layer and managed wrappers for this.  Obviously if you care about something besides JPGs and AVIs WIA may not work for you.  Also, WIA requires that the device you’re speaking to has a WIA driver.  WIA drivers are certainly very common for the appropriate device types, but when you are distributing desktop software that depends on an OEM’s drivers you’re stepping into a world of pain (Not a world of Payne, which is actually a wonderful place).    Can the OEM give you merge-modules for the install?  Are their drivers buggy?  Be sure to treat your Support Group very well if you go down this path.

The Problem Solution: Shell Extensions and Managed Code

I’m going to focus on a simple subset of the overall problem.  Despite the perceived simplicity of the final solution, I couldn’t find a single working example of this online so if you got here via Goolge you’ve hit the jackpot.   Suppose you have an SD card, an SD card reader, and some family photos on said SD card.  You would like a .NET program to automatically create thumbnails of these when the SD card is inserted into your card reader.

Step 1

Step 1 is to read some obscure documentation found here: http://msdn.microsoft.com/msdnmag/issues/01/11/autoplay/

The first part of this is easy to follow. The Windows Autoplay functionality mostly resides in the sub keys of HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\AutoplayHandlers\EventHandlers\

Oddly enough, the names of these sub-keys are almost meaningful and you can do some Googling Windows Live Searching to get an idea of what exactly will cause this or that handler to be invoked.  In our case, the key we want for XP and Vista is ShowPicturesOnArrival.  Windows scans the SD card after it’s inserted and finds JPGs or whatever and checks this sub key to determine what choices the Shell should offer the user.  What can be gleaned from the MSDN article in Step 1 is that we can make up a Handler name, so I’ll call mine TestPhotoHandler.  This “TestPhotoHandler” must correspond to a sub key of HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\AutoplayHandlers\Handlers (../../Handlers from ShowPicturesOnArrival), which will in turn contain some values that describe what TestPhotoHandler is to the Windows Shell.

Step 2

Step two is to populate our Handler.

·         Action=what will appear to the user they are doing with this handler. 

·         DefaultIcon=Pretty self explanatory.  You can also use the Foo.exe,-1 syntax to specify an embedded resource. 

·         Provider=Name of your program

·         InvokeProgID, InvokeVerb=the interesting part, keep reading.

o   Note that it would be FAR too easy to just let you include the path to your .exe or .dll here…

With just the above work done, you can see the following behavior in Windows:

Step 3

Telling Windows where to go from here is far more difficult than it needs to be, and I could not find a single working example of getting this stuff to work with managed code.  In addition to telling Windows how to find our .NET program, Windows must be able to communicate with our program.  There are three interfaces that are interesting for these purposes:

1.       IHWEventHandler

2.       IHWEventHandler2

3.       IDropTarget

These are COM interfaces.  Because I keep meaning to get back into C++ development I always install C++ accessories in Visual Studio.  This means I have the various COM header and IDL files installed, and I can dig up some useful information.  Recall that .NET classes can implement COM interfaces as long as you know the GUID of the COM interface.  Here, then, is my IDropTarget interface definition for .NET objects.

    [ComImport,

     InterfaceType(ComInterfaceType.InterfaceIsIUnknown),

     Guid("00000122-0000-0000-C000-000000000046")]

    public interface IDropTarget

    {

        [PreserveSig()]

        int DragEnter(IntPtr pDataObj,               //  IDataObject

                      ulong grfKeyState,             //  DWORD

                      POINTL pt,

                      ref ulong pdwEffect);          //  DWORD*

 

        [PreserveSig()]

        int DragOver(ulong grfKeyState,              //  DWORD

                     POINTL pt,

                     ref ulong pdwEffect);           //  DWORD*

 

        [PreserveSig()]

        int DragLeave();

 

        [PreserveSig()]

        int Drop(IntPtr pDataObj,                    //  IDataObject

                 ulong grfKeyState,                  //  DWORD

                 POINTL pt,

                 ref ulong pdwEffect);               //  DWORD*

 

    }

 

    [StructLayout(LayoutKind.Sequential)]

    public struct POINTL

    {

        public POINTL(long xx, long yy) { x = xx; y = yy; }

        public long x;

        public long y;

        public override string ToString()

        {

            String s = String.Format("({0},{1})", x, y);

            return s;

        }

    }

 

PointL was copied from Dino Esposito’s excellent article on Shell Extensions on The Server Side.  At any rate, these three attributes on IDropTarget basically tells COM that we can speak the COM lingo and have the behaviors specified by the Interface identified by this GUID.

Step 4

Next we’ll implement the IDropTarget Interface.  The implementation can supposedly reside either in an .exe or .dll, but for our purposes it only works and is only safe when the implementation is on the Entry Point of an .exe project.  So,

    [Guid("C1D0D2F6-5F45-4462-9835-C463AFE367E3")]

    [ComVisible(true)]

    public class Program : IDropTarget

    {

        /// <summary>

        /// The main entry point for the application.

        /// </summary>

        static void Main()

        {

There are some things to take note of here.  Since we wish to enter the world of COM (and COM is love…) we put a ComVisible attribute on the class.  The default Program created for you in VS2008 has Program as a static class, which cannot have attributes so we’ve removed that.  We also have used the Tools in Visual Studio to create a GUID for this class.  We are using an API known as “HackingtheRegistrySinceNoRealRegistrationAPIExists” and guids are the coin of the realm.  I happen to hate this API but options are scarce.

Step 5

The next step is to register our code in such a way that it is findable via the appropriate magic.  For .NET programs RegAsm.exe is our registrar of choice.  Note that RegAsm will pout if your Assembly is not strongly named.  If we now “Regasm.exe /codebase LaunchTestProg.exe” we get some interesting registry settings created for us.  Under HKEY_CLASSES_ROOT\CLSID we find our Program’s GUID, along with the following:

A sub key named “ProgId” containing the fully qualified class name of the .NET program we’d like to run is nestled beneath our class id guid.  This is part of the explanation for the values in Step 2.  Continuing to search for our GUID in the registry we find that HKEY_CLASSES_ROOT\LaunchTestProg.Program has been created for us. 

Step 6

Rather than be clever I’ll just show the final piece we need to create.  The Shell depends on various lookups and naming conventions to determine what it needs to do. 

Below HKEY_CLASSES_ROOT\LaunchTestProg.Program, we need to create some registry keys and values ourselves.  We create sub keys shell, open, DropTarget, and put the GUID of our .NET program as a clsid attribute of the DropTarget key.  The entire process can now ask the user, find, and Invoke our .NET program when a USB device is inserted and it meets the original Handler criteria.

Further Investigation

Upon testing this, I found some interesting behaviors.  It appears that while the .NET project must be an .exe, what Windows is really doing is instantiating the Program object and invoking the members of IDropTarget on it.  I have not tried too hard to get Main to run.  In normal cases, IDropTarget.DragEnter and then IDropTarget.Drop will be called.  For my particular needs, the arguments to either method are throw-away, but using these should be normal P/Invoke & Marshalling exercises.  If you just want to start some other .NET code you can now do so from the your Drop()  implementation.

I will also warn that you want to read this and understand what it means: http://blogs.msdn.com/junfeng/archive/2005/11/18/494572.aspx

I will also warn that the class library Assembly that contains my IDropTarget interface seems to be file-locked once I actually invoke my program.  Restarting Explorer.exe fixes this, which may be a consideration if you are going to be auto-updating the program in some way.  It seems odd that the .exe containing the actual program is not locked.  I plan on investigating the registry areas of Windows\CurrentVersion\ShellExt\Cached to see if clearing that and firing the appropriate Registry Changed event will unlock the assembly. 

Other Avenues...

You will notice DeviceGroups and DeviceHandlers keys as siblings of the “EventHandlers” subkey.  I also mentioned IHWEventHandler and IHWEventHandler2.  The latter is merely