创建处理

对于文件系统,大多数有趣的安全工作都发生在IRP_MJ_CREATE过程中。 此步骤必须分析传入请求,确定调用方是否具有执行该操作的适当权限,并在适当时授予或拒绝该操作。 幸运的是,对于文件系统开发人员,大多数决策机制是在安全参考监视器中实现的。 因此,在大多数情况下,文件系统只需调用相应的安全引用监视器例程来正确确定访问权限。 如果文件系统无法在必要时调用这些例程,并且未适当地向调用方授予访问权限,则会出现文件系统风险。

对于标准文件系统(如 FAT 文件系统)而言,作为标准文件系统的一IRP_MJ_CREATE检查主要是语义检查。 例如,FAT 文件系统具有许多检查,以确保IRP_MJ_CREATE文件或目录的状态允许进行其他处理。 FAT 文件系统进行这些检查包括只读媒体检查 (例如,不允许尝试对只读媒体执行破坏性的"创建"操作(如覆盖或取代)) 、共享访问检查和 oplock 检查。 此分析的最困难部分之一是意识到,文件级别的一个 ((例如) )的操作实际上可能由于卷级别的不同级别资源 (状态(例如) )而禁止。 例如,如果另一个进程以独占方式锁定了卷,则可能无法打开文件。 要检查的常见情况包括:

  • 文件级别打开是否与卷级别状态兼容? 必须遵循卷级锁定。 因此,如果一个进程持有排他卷级别锁,则只有该进程中的线程才能打开文件。 不允许来自其他进程的线程打开文件。

  • 打开的文件级别是否与媒体状态兼容? 某些"创建"操作在"创建"操作过程中修改文件。 这包括覆盖、取代,甚至更新文件上的上一次访问时间。 不允许对只读媒体执行这些"创建"操作,并且不会更新上次访问时间。

  • 卷级别打开是否与文件级别状态兼容? 如果卷上已打开现有文件,则不允许打开独占卷。 对于新开发人员来说,这是一个常见问题,因为他们尝试打开卷并发现它失败。 如果此操作失败,FSCTL_DISMOUNT_VOLUME使打开的句柄失效并强制卸载,从而允许对新装载的卷进行独占访问。

此外,文件属性必须兼容。 无法打开具有只读属性的文件进行写入访问。 请注意,展开泛型权限扩展后,应检查所需的访问权限。 例如,FASTFAT 文件系统中的此检查位于 FatCheckFileAccess 函数中 (请参阅 WDK 包含的 fastfat 示例中的 Acc且sup.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 文件系统从 WDK 包含) 的 fastfat 示例中的 Acc且sup.c 的 FatCheckFileAccess 函数中识别到的 (:

下面的代码示例演示了文件系统安全性的重要概念。 检查以确保传递给文件系统的内容不会超出预期范围。 从安全性的角度来看,保守且正确的方法是,如果不了解访问请求,应拒绝该请求。

    //
    // 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,通过该节的后续写入操作也会作为对文件对象的IRP_MJ_WRITE操作发送。 换句话说,当 ObReferenceObjectByHandle 将文件句柄转换为 Nt 系统服务入口点的相应文件对象时,将执行访问强制。

文件系统中还有两个必须执行类似于"创建"处理的语义安全检查的位置:

  • 在重命名或硬链接处理期间。

  • 处理文件系统控制操作时。

后续部分将讨论重命名处理和文件系统控制处理。

请注意,这不是与"创建"处理相关的语义问题的详尽列表。 本部分的目的是提醒文件系统开发人员注意这些问题。 必须识别特定文件系统的所有语义问题,实现这些问题以满足特定语义,并进行测试以确保实现能够处理各种情况。