IRP_MJ_DIRECTORY_CONTROL でのセキュリティ チェック
特定のディレクトリ制御操作、特に変更通知を処理する操作を処理するときは、セキュリティを考慮する必要があります。 セキュリティ上の懸念は、ディレクトリ変更通知により、変更された特定のファイルに関する情報が返される可能性があることです。 ユーザーがディレクトリへのパスをトラバースする権限を持っていない場合、変更に関する情報をユーザーに返すことはできません。 それ以外の場合、ユーザーは、ユーザーが持つべきではないディレクトリに関する追加情報を学習するメカニズムを持ちます。
ファイル システム ランタイム ライブラリによるディレクトリ変更通知のサポートにより、ファイル システムはディレクトリ変更通知を返す前にトラバース チェックを実行するコールバック関数を指定できます。 このコールバック関数は多数のパラメータを受け取ります。 セキュリティを考慮すると、次の 3 つのパラメータが重要です。
NotifyContextは、変更通知がアクティブなディレクトリのコンテキストです。 これは、FsRtlNotifyFullChangeDirectory への呼び出しに渡される FsContext パラメーターになります。
TargetContext は、変更されたファイルのコンテキストです。 これは、ファイル システムが FsRtlNotifyFilterReportChange を呼び出すときにファイル システムによって渡される TargetContext パラメータになります。
SubjectContext は、ディレクトリ変更通知を要求するスレッドのセキュリティ コンテキストです。 これは、ディレクトリ変更通知呼び出しが FsRtlNotifyFullChangeDirectory に対して行われたときに、ファイル システムによってキャプチャされたサブジェクト セキュリティ コンテキストです。.
変更が発生すると、ファイル システムはこれをファイル システム ランタイム ライブラリに通知します。 次に、ファイル システム ランタイム ライブラリは、ファイル システムによって提供されるコールバック関数を呼び出し、呼び出し元に変更に関する情報が与えられるかどうかを確認します。 ファイルシステムは、呼び出し元にチェックが必要な場合にのみコールバック関数を登録する必要があることに注意してください。 これは、呼び出し元のセキュリティ トークンの TOKEN_HAS_TRAVERSE_PRIVILEGE で示されているように、呼び出し元で SeChangeNotifyPrivilege が有効になっていない場合に当てはまります。
コールバック関数内で、ファイル システムは、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;
}
このルーチンは、セキュリティ情報をキャッシュするファイル システム、またはファイルとディレクトリを追跡するための異なるデータ構造を持つファイル システム (たとえば、ファイルとディレクトリ間のリンクを追跡するための構造を使用するファイル) では大幅に異なる可能性があります。 このサンプルでは、例を簡略化するために、リンクをサポートするファイル システムは考慮されていません。