Guidance for Implementing Callback Functions

Regardless of the design of your Solution, there are four important things we'd like you to keep in mind in terms of the design and implementation of Client Policy DLL callbacks.  Those four things, in no particular order, are:

      All Policy DLL callback code that you implement must be thread-safe.  FesfPolicy dynamically grows (and shrinks) the pool of worker threads it uses to call Policy DLL callbacks.  FESF currently sets the maximum number of active callback threads to 122 on 32-bit systems, and to 244 on 64-bit systems.  These maxima are subject to change, up or down, in subsequent releases of FESF.  During pre-release testing, we regularly hit the maximum number of active worker threads.  A design which takes maximum advantage of the parallelism offered by FESF, and an efficient, scalable, locking scheme where access to shared data is required, are a must in your Solution.

      Callbacks to your policy DLL must complete "promptly."  Remember, when FesfPolicy calls your Policy DLL it's blocking a kernel-mode operation, typically a user's request to open or create a file. For the Solution and the overall Windows system on which the Solution is running to exhibit good performance, prompt and efficient processing is a must.  A Solution architecture that judiciously caches information, including Policy decisions, key material, and user Security Group membership, is strongly advised.  While we're not fans of premature optimization, we would encourage you to at least take these precepts into account as part of your Solution's initial design.

You might reasonably ask "What time period, precisely, does 'promptly' imply?"  Unfortunately, we don't have a good answer for you.  By promptly, we really mean "as soon as practically possible for your Solution."  Each workload will be different, and your Solution has to meet your own design and usability goals.  However, from a systems perspective, we would advise targeting a small number of seconds (in the low single digits) as the maximum time for a Policy DLL callback to complete under heavy system load.  This should yield acceptable performance.  This is provided only as a guideline to aid you in your design.

One absolute maximum that we can warn you about is that used by the FESF Kernel Mode Components.  These components set an arbitrary maximum of 30 seconds that they will wait for a reply from user-mode.  One of our developers describes this interval as "a virtual eternity", and indeed it is the uppermost bound that can be expected for a reply even on a severely degraded system.  After this period of time, an error message is logged to the Windows Event Log and the timed-out operation is completed with an error (access denied).  While this will ensure the system remains running, returning errors from a Policy DLL callback is rarely desirable, as further described below.

      Avoid returning failure codes when possible.  As described in the section above entitled Returning Failure from Policy DLL Callbacks, it is almost always a bad idea to return a failure code from the PolGetPolicyNewFile, PolGetKeyNewFile, PolGetPolicyExistingFIle, and PolGetKeyFromHeader Policy DLL callbacks. We would advise you to restrict failure returns to those conditions which are unforeseeable and catastrophic.  We would recommend not returning failure statuses to FESF callbacks in transient conditions such as lost connections or slow responses from your key management server.  Obviously, only you understand your Solution and its requirements.  But, at the very least, please do not return an error from these callbacks as an alternative form of implementing file access security.

      Be conscious of the potential for reentrancy and avoid it.  Bear in mind that your Policy DLL's callbacks are executing while a Windows system service (typically a CreateFile operation) is pending.  This means you must avoid the potential for reentrancy problems.  As a very simple example, consider what might happen if you create a new file as a result of being called in your PolGetPolicyNewFile callback.  In this example, assuming your policy determinations are made in a separate process (similar to the way the OSR Sample Solution works) your PolGetPolicyNewFile callback would get called-back endlessly (each time, creating a new file which then results in another callback).  Not good! 

Note that to preserve FESF integrity (and to eliminate the most obvious potential causes of endless callbacks), FESF kernel-mode code suppresses all calls to the Policy DLL for I/O operations performed within the context of the FESF Policy Service itself.  So, in the above example, creating a new file directly within the Policy DLL’s PolGetPolicyNewFile callback (and not in a separate process) would NOT generate a reentrant call to PolGetPolicyNewFile.