HOWTO: PreCondition an ISAPI Extension DLL

Question:

Hello

We have a legacy x64 ISAPI extension that runs without problems in Win 2003 64bit - IIS 6.0.

But in Win2008 EBS Security Manager setup, IIS 7.0 Default Application Pool's WOW64 / Enable32BitApp property is enabled by default, and this makes x64 ISAPI extension DLL to fail loading.

By setting the Enable32BitApp option as false in application pool settings, the problem gets resolved.

However we want to speicify ISAPI extension explicitly to be loaded as x64 module using "precondition='bitness64' " settings like ISAPI filters, so that ISAPI extension works no matter what application pool is configured for (x64 only, or both x64 and x86)

Could anyone please suggest how we can do this or the app cmd that would allow us to do it.

Answer:

Technically, preConditions only apply on a per-GlobalModule, per-Module, per-Handler, or per-ISAPI Filter basis. This is because globalModules, modules, handlers, and ISAPI Filters have to be explicitly configured within IIS to function. ISAPI Extensions simply need to be dropped into a web-accessible folder and then "allowed to execute" via Executable execution permission and IsapiCgiRestriction. There is no per-ISAPI Extension configuration to stick preConditions.

However, as I will illustrate shortly, it is STILL possible to apply preConditions on a per-ISAPI Extension basis in a fairly clean and clear manner.

Some people would propose that it would be nice to stick preCondition onto IsapiCgiRestriction since it is a sort of per-ISAPI Extension configuration, but IsapiCgiRestriction is really a security configuration which we hardly want to preCondition. Besides, what happens if notListedIsapisAllowed="true" and we still want to preCondition an ISAPI Extension - now we have to add a bogus entry for preCondition? Anyways, this is getting complicated very quickly, a sure sign that the proposed design has some fundamental logical flaw.

Common Misconceptions about Bitness and preConditions 

Now, before I get into how to setup per-ISAPI Extension preCondition, I want to clarify some of the misconceptions in your question about Bitness and preCondition. Using preCondition="bitness64" does NOT:

  1. Explicitly load an ISAPI Extension as x64 module
  2. Make an ISAPI work no matter what the application pool is configured for
  3. Allow "both x64 and x86" of ANYTHING

A preCondition is a simple filtering mechanism on the affected IIS configuration. What preCondition="bitness64" means is that the affected configuration is ONLY applicable in a 64bit Application Pool. A 64bit Application Pool will only see globalModule, module, handler, and isapiFilter with either bitness64 or no bitness preCondition. It will not see any globalModule, module, handler, and isapiFilter configuration with the bitness32 preCondition.

Thus, a preCondition does NOT cause an ISAPI to be explicitly loaded as any bitness. LoadLibrary() will just load the resolved DLL name into the process's address space, and if the bitness do not match, fail. Likewise, it cannot make the ISAPI work no matter what, because if the bitness does not match, it fails. Finally, Windows does not allow both 32bit and 64bit code to co-exist in the same process, and IIS does not allow an Application Pool to be both 32bit and 64bit.

What is a correct way to use the bitness preConditions? Check out the various pre-defined "-64" handlers when ASP.Net Feature support is installed. For example, the svc-ISAPI-2.0 and svc-ISAPI-2.0-64 handlers. Notice that the bitness32 preCondition applies to a 32bit ISAPI, and bitness64 preCondition applies to a separate 64bit ISAPI. This dual configuration allows the same feature to work with both 32bit and 64bit Application Pools with no additional configurations changes in-between. Remember how you had to run aspnet_regiis.exe from the correct Framework bitness directory every time you changed Application Pool bitness with .Net Framework 2.0 on IIS6 in 64bit Windows, or else you would see 503 Service Unavailable failure? No longer necessary with preConditions on IIS7 and things just work!

PreConditioning an ISAPI Extension DLL

As I mentioned earlier, per-ISAPI Extension preCondition can be accomplished by leveraging preCondition support of handlers. The following configuration shows how to request an ISAPI Extension name "MyISAPI.dll" and have it work no matter the bitness of the Application Pool. It requires a 32bit version and 64bit version of the same DLL, named MyISAPI32.dll and MyISAPI64.dll, respectively.

 <handlers>
  ...
  <add name="MyISAPI-64" path="MyISAPI.dll" verb="*" modules="IsapiModule" scriptProcessor="C:\inetpub\wwwroot\bin\MyISAPI64.dll" resourceType="File" requireAccess="Execute" preCondition="bitness64" />
  <add name="MyISAPI-32" path="MyISAPI.dll" verb="*" modules="IsapiModule" scriptProcessor="C:\inetpub\wwwroot\bin\MyISAPI32.dll" resourceType="File" requireAccess="Execute" preCondition="bitness32" />
  ...
</handlers>

Looking at the key configuration details:

  • Having two handlers, one with bitness32 preCondition on the 32bit MyISAPI32.dll, the other with bitness64 preCondition on the 64bit MyISAPI64.dll, assures that only one of these handlers with the correct bitness apply to any given Application Pool
  • path="MyISAPI.dll" means that end consumers only request /MyISAPI.dll while the dynamic 32/64bit wiring happens underneath the covers via the bitness preCondition
  • requireAccess="Execute" makes Execute permissions necessary to execute an ISAPI Extension DLL in a given URL namespace

The combination of preConditions, path-remapping, and requireAccess gives the illusion of a single named ISAPI DLL which transparently works with similar requirements as ISAPI Extension regardless of Application Pool bitness. Yes, don't forget to set IsapiCgiRestriction on both MyISAPI64.dll and MyISAPI32.dll as well as enable "Execute" permission on your virtual directory... :-)

Cheers.

//David