创建处理
对于文件系统,大多数有趣的安全工作都发生在 IRP_MJ_CREATE 处理期间。 此步骤必须分析传入请求,确定调用方是否具有执行操作的适当权限,并根据需要授予或拒绝操作。 幸运的是,对于文件系统开发人员来说,大多数决策机制是在安全参考监视器中实现的。 因此,在大多数情况下,文件系统只需调用相应的安全参考监视器例程即可正确确定访问权限。 如果文件系统无法根据需要调用这些例程,并且不恰当地向调用方授予访问权限,则会发生文件系统的风险。
对于标准文件系统(如 FAT 文件系统),作为IRP_MJ_CREATE一部分进行的检查主要是语义检查。 例如,FAT 文件系统进行大量检查,以确保根据文件或目录的状态允许IRP_MJ_CREATE处理。 FAT 文件系统进行的这些检查包括检查只读媒体 (例如,不允许在只读介质上尝试执行破坏性的“创建”操作,如覆盖或取代,) 、共享访问检查和 oplock 检查。 此分析中最困难的部分之一是认识到,某个级别的操作 (文件级别,例如,由于不同级别的资源的状态 (卷级别(例如) ),实际上可能不允许) 。 例如,如果另一个进程以独占方式锁定了卷,则可能无法打开文件。 检查的常见情况包括:
文件级别打开是否与卷级别状态兼容? 必须遵循卷级锁定。 因此,如果一个进程具有独占卷级别锁,则只有该进程中的线程可以打开文件。 不得允许其他进程的线程打开文件。
文件级别是否与媒体状态兼容? 某些“创建”操作修改文件作为“创建”操作的一部分。 这将包括覆盖、取代,甚至更新对文件的最后一次访问时间。 只读媒体上不允许执行这些“创建”操作,并且上次访问时间不会更新。
卷级别打开是否与文件级别状态兼容? 如果卷上打开了现有文件,则不允许独占卷打开。 对于新开发人员来说,这是一个常见问题,因为他们尝试打开卷并发现卷失败。 如果此操作失败,FSCTL_DISMOUNT_VOLUME可用于使打开的句柄失效并强制卸载,从而允许对新装载的卷进行独占访问。
此外,文件属性必须兼容。 无法打开具有只读属性的文件进行写入访问。 请注意,扩展泛型权限后,应检查所需的访问权限。 例如,FASTFAT 文件系统中的此检查位于 FatCheckFileAccess 函数中, (查看 WDK 包含) fastfat 示例中的 Acchksup.c 源文件。
下面的代码示例特定于 FAT 语义。 实现 DACL 的文件系统将使用安全参考监视器例程 (SeAccessCheck 执行额外的安全检查,例如.)
//
// check for a read-only Dirent
//
if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_READ_ONLY)) {
//
// Check the desired access for a read-only Dirent
// Don't allow
// WRITE, FILE_APPEND_DATA, FILE_ADD_FILE,
// FILE_ADD_SUBDIRECTORY, and FILE_DELETE_CHILD
//
if (FlagOn(*DesiredAccess, ~(DELETE |
READ_CONTROL |
WRITE_OWNER |
WRITE_DAC |
SYNCHRONIZE |
ACCESS_SYSTEM_SECURITY |
FILE_READ_DATA |
FILE_READ_EA |
FILE_WRITE_EA |
FILE_READ_ATTRIBUTES |
FILE_WRITE_ATTRIBUTES |
FILE_EXECUTE |
FILE_LIST_DIRECTORY |
FILE_TRAVERSE))) {
DebugTrace(0, Dbg, "Cannot open readonly\n", 0);
try_return( Result = FALSE );
}
FASTFAT 实现的一个更微妙检查是确保调用方请求的访问权限是 FAT 文件系统在 Acchksup.c 的 FatCheckFileAccess 函数中从 WDK 包含) 的 fastfat 示例中 (知道的访问:
以下代码示例演示了文件系统安全性的重要概念。 检查以确保传递到文件系统的内容不会超出预期范围。 从安全的角度来看,保守和适当的方法是,如果你不了解访问请求,则应拒绝该请求。
//
// Check the desired access for the object.
// Reject what we do not understand.
// The model of file systems using ACLs is that
// they do not type the ACL to the object that the
// ACL is on.
// Permissions are not checked for consistency vs.
// the object type - dir/file.
//
if (FlagOn(*DesiredAccess, ~(DELETE |
READ_CONTROL |
WRITE_OWNER |
WRITE_DAC |
SYNCHRONIZE |
ACCESS_SYSTEM_SECURITY |
FILE_WRITE_DATA |
FILE_READ_EA |
FILE_WRITE_EA |
FILE_READ_ATTRIBUTES |
FILE_WRITE_ATTRIBUTES |
FILE_LIST_DIRECTORY |
FILE_TRAVERSE |
FILE_DELETE_CHILD |
FILE_APPEND_DATA))) {
DebugTrace(0, Dbg, "Cannot open object\n", 0);
try_return( Result = FALSE );
}
幸运的是,对于文件系统,在初始创建处理过程中完成安全检查后,I/O 管理器将执行后续安全检查。 因此,例如,I/O 管理器可确保用户模式应用程序不会对仅针对读取访问权限打开的文件执行写入操作。 事实上,文件系统不应在IRP_MJ_WRITE调度例程期间尝试对文件对象强制实施只读语义,即使它只是为了读取访问而打开的。 这是因为内存管理器将特定文件对象与给定节对象关联的方式。 随后通过该部分的写入将作为对文件对象的IRP_MJ_WRITE操作发送,即使文件是只读打开的。 换句话说,当 ObReferenceObjectByHandle 将文件句柄转换为 Nt 系统服务入口点上的相应文件对象时,将执行访问。
文件系统中还有两个其他位置,必须进行语义安全检查,类似于“创建”处理:
在重命名或硬链接处理期间。
处理文件系统控制操作时。
后续部分将讨论重命名处理和文件系统控制处理。
请注意,这不是与“创建”处理相关的语义问题的详尽列表。 本部分的目的是提醒文件系统开发人员注意这些问题。 必须识别特定文件系统的所有语义问题,实现以满足特定语义,并经过测试以确保实现处理各种情况。