Symfony 2 Crash Course
Native Linux Space Warfare: Freespace 2
Nice n' Easy JQuery Image Rotator
Book Review: How to Implement Design Patterns in PHP
Installing Xdebug for use with Eclipse or Netbeans on Linux
A Simple ISAPI Filter for Authentication on IIS

A Simple ISAPI Filter for Authentication on IIS

Wednesday, 12 December 07, 12:00 am
The MDSN samples include a C++ project for building a ISAPI DLL which performs authentication for web resources against a text file. This project, AuthFilt, is one of the samples supplied with the .NET Platform SDK, available from here. Once you've installed the SDK, the sample code is located in the AuthFilt folder at Program Files\Microsoft Platform SDK\Samples\Web\iis\filters.

The filter is something I want to look at because it could be adapted to provide a readily customisable means of securing web folders on Windows. So I need to check it works ok, and whether or not I'll be able to modify it to our needs.

During the investigatory phase, I'll be using Visual C++ express edition, available from this link.

compton

9:31 am, Wednesday, 12 December 07

When you open the project/solution in VS Express 2008, it will warn you that it will need to convert the project to the current format, which it seems to manage okay.

The DLL needs to expose a couple of functions in order for IIS to recognise it, namely GetFilterVersion() and HttpFilterProc(). These functions exist fine, but after conversion to 2008, the project did not export them correctly. To fix it, right-click on the project in the Solution Explorer, and click Properties. Expand the Linker thingee, and select the Input item, and enter AuthFilt.def in the Module Definition File box.

Once the DLL has been successfully compiled, installation on IIS is straightforward. Open up the IIS manager, right click on the Web Sites folder in the left-hand pane (underneath the local computer icon), choose Properties and open the ISAPI Filters tab. Click the Add button to the right, and browse to your compiled DLL (which will be in the Release or Debug folder of your VS project). Give the filter a name, such as AuthFilt, and click OK (twice). Restart IIS. To check the filter is installed correctly, open up the ISAPI Filters tab again, and confirm that the filter is now listed alongside a green arrow pointing up. If it has a red arrow pointing down, there was some problem loading the filter. Go to the System Log and look for an error message from W3SVC for more iformation. The data part of the error message will give the specific error code encountered, with 7e meaning IIS could not find the DLL using the path you specified when adding it. 7f means that the DLL does not expose the required functions: make sure you have added the AuthFilt.def file to the Linker configuration in Project Properties (just enter this filename as the Module Definition File in the Input subcategory of the Linker properties).

Be aware that once installed on IIS, you will need to stop IIS whenever you need to recompile the filter, otherwise VS will not be allowed access when it tries to overwrite the DLL.

compton

11:08 am, Wednesday, 12 December 07

The AuthFilt code works by intercepting all IIS authentication notifications ie whenever an attempt is made to access a folder with restricted access. It then calls ValidateUser() with the supplied credentials, setting the fAllowed flag according to the result.

The ValidateUser() function works by checking if the passed credentials match either the cached user list, or the user list file (in userdb.txt). If they do match, the corresponding NT User name and password are copied over to the pszUser and pszPassword attributes of the HTTP_FILTER_AUTHENT struct passed into HttpFilterProc as the pvData parameter. This results in the user adopting the NT credentials of the user specified in the userdb.txt file.

compton

12:23 pm, Wednesday, 12 December 07

For my purposes, the problems with the code are:
  1. Requires IIS authentication to be enabled for a protected resource
  2. Requires knowledge of a system user account name and password for the userdb.txt file
Note that even if anonymous access is enabled for a folder, the filter will still be triggered at the SF_NOTIFY_AUTHENTICATION phase of the request: in fact this notification only fires for anonymous requests and for requests with an authorization header that specifies Basic authentication.

To demonstrate, it is possible to make the filter deny all anonymous requests on the server by simply changing the code that handles anonymous request (at the top of the HttpFilterProc() function) from:

return SF_STATUS_REQ_NEXT_NOTIFICATION;

to:

SetLastError(ERROR_ACCESS_DENIED);
return SF_STATUS_REQ_ERROR;


If we're going to use a custom solution based on AuthFilt, I would need to add a security check at some point during the request. The check would first determine whether the requested resource is protected or not. if not, then allow access, otherwise trigger an authentication dialog in the browser. One way to trigger such a thing would be to send out a raw HTML authentication header ie to sidestep IIS security and build the headers in code.

Of course, I'd only need to invoke the login dialog if the user is not already authorised ie if there is no Authorization header already set.

There appear to be two suitable places for the security check. We could handle it within the SF_NOTIFY_AUTHENTICATION phase, or within the SF_NOTIFY_URL_MAP stage.

SF_NOTIFY_AUTHENTICATION actually fires after SF_NOTIFY_URL_MAP, so we should know the physical path of the requested resource at this point, which should allow us to verify whether or not it is secured.

Having done some investigation about the above two events, I find it is possible to check whether a requested resource is secured quite easily by intercepting the SF_NOTIFY_URL_MAP event. IIS passes the request URL and the corresponding physical location at this point, which can be used to check for a settings file in the resource's folder.

It is also not too hard to force a client authentication in a SF_NOTIFY_AUTHENTICATION handler, because IIS passes in a struct containing user credentials.

Please enter your comment in the box below. Comments will be moderated before going live. Thanks for your feedback!

Cancel Post

/xkcd/ Schwa