Recently, someone asked us about how security was assigned to new files by the file system. Of course, this only applies to NTFS because it is the only native Windows file system that actually supports security descriptors on individual files and directories. Presumably, this is also how other third party file systems handle security as well.
One important note: the details of how security is implemented varies by the specific version of Windows that implements the security. For example, the NTFS driver included in more recent versions of Windows uses a different mechanism for storing individual security descriptors than earlier versions (the goal being to minimize the number of copies that are present on disk).
When a new file or directory is created - using IRP_MJ_CREATE - the file system uses the security descriptor from the containing directory as well as the security descriptor passed in by the calling application. The application provided security descriptor arrives in the ACCESS_STATE structure, embedded within the Create Parameters field of the I/O Stack Location: IoStackLocation->Parameters.Create.SecurityContext->AccessState->SecurityDescriptor. These two security descriptors are passed into SeAssignSecurity (note that none of the file systems appear to use SeAssignSecurityEx at the present time), along with the security credentials of the caller (the SubjectSecurityContext) and the standard generic rights mapping (IoGetFileObjectGenericMapping) used for files, as well as an indicator of whether or not the new object is a directory.
This function (SeAssignSecurity) returns a new security descriptor (allocated from pool) to the file system. From the file system perspective, this is ideal because there is no need to "understand" the details of how security descriptors are constructed - this is entirely up to the OS. If the application does not provide a new security descriptor, presumably the container descriptor is used and those various inheritance properties are interpreted as appropriate.
Typically, the underlying file system will store the security descriptor in a compact form. For example, it is common for there to be only a single copy of each unique security descriptor because most files and directories will use the same one or two descriptors. Thus, this minimizes the storage overhead associated. Of course, there is nothing that requires this implementation.
Once the security descriptor has been established, it is subsequently fetched and stored using the IRP_MJ_QUERY_SECURITY and IRP_MJ_SET_SECURITY operations. Filters interested in security must thus observe these two operations as well as IRP_MJ_CREATE in order to watch how security is managed and manipulated.