Security Checks
The CBFilter class offers very flexible security handling. During Control Events, applications can use information obtained from security-related methods to determine whether a request should be allowed as-is, modified before continuing, or denied immediately.
CBFilter provides a number of methods that applications can use to help implement security checks. The most notable of these methods are GetOriginatorProcessName and GetOriginatorProcessId, which return the name and PID of the process that initiated the request; and GetOriginatorToken, which returns the system-defined security token of the process that initiated the request.
The latter is particularly useful when used with various methods in the Windows API, such as the GetTokenInformation function, which can be used to obtain various pieces of information about the object the token is associated with.
Effective security enforcement does not require that all event handlers perform security checks. Applications are technically free to deny almost any file-related event due to a failed security check. However, it isn't actually meaningful to perform security checks in all event handlers in the first place, and unnecessary security checks will decrease an application's overall performance.
For example, it makes sense to validate access rights in the BeforeCreateFile, AfterCreateFile, BeforeOpenFile, and AfterOpenFile event handlers, but not in the AfterReadFile or BeforeWriteFile event handlers. If an application denies a file create/open request made by some process, then that process won't be able to make a subsequent read/write request; therefore, applications can safely assume that all read/write requests come from processes whose access rights they have already verified. Similar logic can be applied for directories and directory enumerations. (Note that this is not an exhaustive set of use-cases; each application's needs will differ.)
As stated above, applications are free to allow/modify/deny any request based on security checks. However, they must not "selectively alter" filesystem information based on these checks, it's "all or nothing". Here are some clarifying examples to help guide implementation:
- A filesystem object cannot appear to exist to process A, but appear nonexistent to process B.
- A filesystem object cannot be reported as a file to process A, but as a directory to process B.
- A filesystem object's metadata, absent of actual changes, must be consistent between requests; e.g., if a file's size is reported as 1 KB to process A, then it must also be reported as 1 KB to process B.
- A file's contents, absent of actual changes, must also be consistent between requests; e.g., for a file whose size is reported as 1 KB, exactly 1024 bytes must be returned when the file is read, and those 1024 bytes must be exactly the same (including ordering) regardless of which process is doing the reading.