Microsoft Internet Security and Acceleration Server 2004 SDK |
You can use these best practices to create more efficient application filters.
When working with BSTR strings, the implementing entity should be responsible for allocating and freeing memory. For example, if the Firewall service allocates the memory, it should also free it.
A likely scenario is the case where an application filter needs to use a BSTR held by the Firewall service. The Firewall service should provide a pointer to the BSTR, so that the application filter can allocate memory, copy the BSTR, and be responsible for freeing the memory. This approach reduces the likelihood of memory leaks.
For example, there may be a race between calls to Detach (IFWXDataFilter::Detach or IFWXSessionFilter::Detach) and other methods. Data filters receive IFWXSocket interfaces when the IFWXDataFilter::SetSockets API is called. Because the Firewall service is multithreaded, more than one thread can use the same IFWXSocket interface simultaneously. Furthermore, one thread may call Detach to release an IFWXSocket interface, while the other thread still attempts to access the interface, perhaps while doing a data pump. Access to the interface should be protected by a locking mechanism, such as a critical section. Because of locking by the Firewall service, deadlock situations may arise. For example, the following code can lead to a deadlock situation:
Lock(); HRESULT hr = spSocket->Close(FALSE); Unlock();
To avoid the deadlock, use this code:
// Copy the member interface pointer to a local "smart" pointer // while the object is locked. CComPtr<IFWXSocket> spSocket; Lock(); spSocket = m_InternalSocket; Unlock(); // lock released to avoid deadlocks // Verify that interface was not released by Detach() if (spSocket != NULL) { // Here it is safe to send, receive, or close the socket. HRESULT hr = spSocket->Close(FALSE); }
There are situations where an application filter limits IP address access, including a:
For security purposes, the application filter defines a range of IP addresses for which a particular connection is allowed. Define that range of IP addresses by using the IFWXIpFilter interface.
Note If you set the range of IP addresses equal to NULL, you are allowing all addresses to connect.
When no data filter is present, the Firewall service user-mode data pump controls resource usage by limiting the number of pending sends on a connection in either direction. For a given communication direction, buffers are received as long as there are less than two pending sends. If there are two pending sends, the receive is delayed until a send is completed. This prevents the accumulation of data buffers when the send rate does not match the receive rate.
When an application filter that uses a data filter is added to the data pump, the send-limiting mechanism does not operate. Your application filter should therefore check whether each send is completed, and should establish a limit as to how many sends can accumulate. When the limit has been reached, your filter should stop calling receive until the number of sends drops below the limit. If you do not create this type of resource usage control mechanism, a large download or a deliberate attack could cause buffers to accumulate, using extensive non-paged pool and user-mode memory. This could result in a denial of service.
During inbound or output emulation, a filter may build up an excessive number of pending I/O operations and overflow the Firewall service. A filter should impose a strict limit on the number of pending I/O operations that it allows.
Application filters should be designed not to allocate excessive amounts of memory per session or request so that they will not become vulnerabale to a denial-of-service (DoS) attack. If filters accumulate data (for example, until there is a complete message), they should set a maximum message size. If this maximum message size is significant, we also recommend that they limit the time during which a message can be stored.
Application filters should never trust data. They should verify that the data is coming from the correct side. For example, they should verify that the client is not sending information that should be coming only from the server. Your filter should also verify that the data has the correct format.
When using hash tables, use salt and other mechanisms to make sure that clients will not be able to craft data that will always be sent to the same bucket.
If an application filter also provides an extension for ISA Server Management, the setup program should allow for separate installation of the administration component and the filter component because ISA Server Management can be run remotely. In particular, ISA Server Management can be run from a remote computer on which ISA Server 2004 is not installed.
During filter registration, the error code HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) should be considered as a success code, and installation should continue in a normal flow. This is done to avoid problems in re-registering an existing filter.
When an application filter is being uninstalled, the filter should try to uninstall all the objects that it registered (events, alerts, etc.), even if some of them no longer exist and an error code is returned. This is done to ensure that maximum cleanup is achieved, and to ensure forward compatibility with future support for array mode.
Application filters should not attempt to write to the stored ISA Server configuration. Application filters are allowed read-only access to the stored ISA Server configuration on computers running Windows Server 2003. Therefore, an application filter that relies on successfully writing to the stored configuration will fail.
Any changes to the stored configuration that are needed for an application filter should be made by the setup process or through the application filter's administrative component.