IRP_MJ_DIRECTORY_CONTROL 上的安全检查
在处理某些目录控制操作时,尤其是处理更改通知的操作时,安全性是一个考虑因素。 安全问题在于,目录更改通知可能会返回有关已更改的特定文件的信息。 如果用户没有遍历目录路径的权限,则无法向用户返回有关更改的信息。 否则,用户现在有一种机制,用于学习用户不应拥有的目录的其他信息。
文件系统运行时库对目录更改通知的支持允许文件系统指定回调函数,以便在返回目录更改通知之前执行遍历检查。 此回调函数采用大量参数。 出于安全考虑,以下三个参数非常重要:
NotifyContext 是更改通知处于活动状态的目录的上下文。 这是传递给 FsRtlNotifyFullChangeDirectory 调用的 FsContext 参数。
TargetContext 是已更改的文件的上下文。 这将是文件系统在调用 FsRtlNotifyFilterReportChange 时传递的 TargetContext 参数。
SubjectContext 是请求目录更改通知的线程的安全上下文。 这是文件系统在对 FsRtlNotifyFullChangeDirectory 进行目录更改通知调用时捕获的主题安全上下文。
发生更改时,文件系统会向文件系统运行时库指示这一点。 然后,文件系统运行时库将调用文件系统提供的回调函数,以验证是否可以向调用方提供有关更改的信息。 请注意,仅当调用方需要检查时,文件系统才需要注册回调函数。 如果调用方未启用 SeChangeNotifyPrivilege,则这是这种情况,如调用方的安全令牌中的TOKEN_HAS_TRAVERSE_PRIVILEGE所示。
在回调函数内,文件系统必须执行遍历检查,从 NotifyContext 参数指定的目录到由 TargetContext 参数指定的已更改的文件。 下面的示例例程执行此类检查。
BOOLEAN
FsdNotifyTraverseCheck (
IN PDIRECTORY_CONTEXT OriginalDirectoryContext,
IN PFILE_CONTEXT ModifiedDirectoryContext,
IN PSECURITY_SUBJECT_CONTEXT SubjectContext
)
{
BOOLEAN AccessGranted = TRUE;
PFILE_CONTEXT CurrentDirectoryContext;
ACCESS_MASK GrantedAccess;
NTSTATUS Status;
PPRIVILEGE_SET Privileges = NULL;
PFILE_CONTEXT TopDirectory;
//
// Nothing to do if there is no file context.
//
if (ModifiedDirectoryContext == NULL) {
return TRUE;
}
//
// If the directory that changed is the original directory,
// we can return , since the caller has access.
// Note that the directory context is unique to the specific
// open instance, while the modified directory context
// represents the per-file/directory context.
// How these data structures work in your file system will vary.
//
if (OriginalDirectoryContext->FileContext == ModifiedDirectoryContext) {
return TRUE;
}
//
// Lock the subject context.
//
SeLockSubjectContext(SubjectContext);
for( TopDirectory = OriginalDirectoryContext->FileContext,
CurrentDirectoryContext = ModifiedDirectoryContext;
CurrentDirectoryContext == TopDirectory || !AccessGranted;
CurrentDirectoryContext = CurrentDirectoryContext->ParentDirectory) {
//
// Ensure we have the current security descriptor loaded for
// this directory.
//
FsdLoadSecurity( NULL, CurrentDirectoryContext);
//
// Perform traverse check.
//
AccessGranted = SeAccessCheck(
CurrentDirectoryContext->SecurityDescriptor,
SubjectContext,
TRUE,
FILE_TRAVERSE,
0,
&Privileges,
IoGetFileObjectGenericMapping(),
UserMode,
&GrantedAccess,
&Status);
//
// At this point, exit the loop if access was not granted,
// or if the parent directory is the same as where the change
// notification was made.
//
}
//
// Unlock subject context.
//
SeUnlockSubjectContext(SubjectContext);
return AccessGranted;
}
对于缓存安全信息的文件系统,或者具有不同数据结构来跟踪文件和目录 (例如,使用结构跟踪文件和目录) 之间的链接的文件,此例程可能大不相同。 为了简化示例,此示例中未考虑支持链接的文件系统。