ファイル システム制御処理
IRP_MJ_FILE_SYSTEM_CONTROL 操作の処理は、ファイル システム内の他の操作で必要なデータ バッファー処理とは異なります。 これは、各操作で CTL_CODE マクロを使用して、制御コードの一部として I/O マネージャーの特定のデータ転送メカニズムが確立されるためです。 さらに、制御コードは呼び出し元が必要とするファイル アクセスを指定します。 このアクセスは I/O マネージャーによって適用されるため、ファイル システムでは制御コードを定義するときに、この問題に特に注意する必要があります。 一部の I/O 制御コード (FSCTL_MOVE_FILE など) は、FILE_SPECIAL_ACCESS を指定します。これは、操作のセキュリティがファイル システムによって直接チェックされることをファイル システムが示すことができるようにするメカニズムです。 FILE_SPECIAL_ACCESS は FILE_ANY_ACCESS と数値的に等しいため、I/O マネージャーでは特定のセキュリティ チェックが行われず、代わりにファイル システムにチェックが任されます。 FILE_SPECIAL_ACCESS は主に、ファイル システムによって追加のチェックが行われるドキュメントを提供します。
いくつかのファイル システム操作が FILE_SPECIAL_ACCESS を指定します。 FSCTL_MOVE_FILE 操作は、ファイル システムの最適化インターフェイスの一部として使用され、FILE_SPECIAL_ACCESS を指定します。 開いていてアクティブに読み取りと書き込みが行われているファイルを最適化できるようにするために、使用されるハンドルには FILE_READ_ATTRIBUTES のみが付与されて共有アクセスの競合が回避されます。 ただし、ディスクが低レベルで変更されるため、この操作は特権操作である必要があります。 解決策は、FSCTL_MOVE_FILE の発行に使用されるハンドルが、直接アクセス ストレージ デバイス (DASD) の開いているユーザー ボリュームであり、それが特権を持つハンドルであることを確認することです。 開いているユーザー ボリュームに対してこの操作が行われていることを確認する FASTFAT ファイル システム コードは、FatMoveFile 関数にあります (WDK に含まれる fastfat サンプルの fsctrl.c ソース ファイルを参照してください)。
//
// extract and decode the file object and check for type of open
//
if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb ) != UserVolumeOpen) {
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
return STATUS_INVALID_PARAMETER;
}
FSCTL_MOVE_FILE 操作で使用される構造体は、移動するファイルを指定します。
typedef struct {
HANDLE FileHandle;
LARGE_INTEGER StartingVcn;
LARGE_INTEGER StartingLcn;
ULONG ClusterCount;
} MOVE_FILE_DATA, *PMOVE_FILE_DATA;
前述のように、FSCTL_MOVE_FILE の発行に使用されるハンドルは "開いている" 状態でのボリューム全体の操作ですが、この操作は実際には MOVE_FILE_DATA 入力バッファーで指定されたファイル ハンドルに適用されます。 このため、この操作のセキュリティ チェックがある程度複雑になります。 たとえば、このインターフェイスでは、ファイル ハンドルを移動されるファイルを表すファイル オブジェクトに変換する必要があります。 この場合、ドライバーの部分について慎重に検討する必要があります。 FASTFAT ではこれを、WDK に含まれる fastfat サンプル内の fsctrl.c ソース ファイルの FatMoveFile 関数で、保護された方法で ObReferenceObject を使用して行います。
//
// Try to get a pointer to the file object from the handle passed in.
//
Status = ObReferenceObjectByHandle( InputBuffer->FileHandle,
0,
*IoFileObjectType,
Irp->RequestorMode,
&FileObject,
NULL );
if (!NT_SUCCESS(Status)) {
FatCompleteRequest( IrpContext, Irp, Status );
DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", Status);
return Status;
}
// Complete the following steps to ensure that this is not an invalid attempt
//
// - check that the file object is opened on the same volume as the
// DASD handle used to call this routine.
//
// - extract and decode the file object and check for type of open.
//
// - if this is a directory, verify that it's not the root and that
// you are not trying to move the first cluster. You cannot move the
// first cluster because sub-directories have this cluster number
// in them and there is no safe way to simultaneously update them
// all.
//
// Allow movefile on the root directory if it's FAT32, since the root dir
// is a real chained file.
// //
Irp->RequestorMode を使用して、呼び出し元がユーザー モード アプリケーションである場合、ハンドルをカーネル ハンドルにできないようにしていることに注意してください。 要求されるアクセスは 0 で、これにより、アクティブにアクセスされているときにファイルを移動できるようにします。 最後に、呼び出しをユーザー モードで行う場合、この呼び出しは正しいプロセス コンテキストで行う必要があることに注意してください。 FASTFAT ファイル システムのソース コードは、fsctrl.c の FatMoveFile 関数でもこれを実施します。
//
// Force WAIT to true. There is a handle in the input buffer that can only
// be referenced within the originating process.
//
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
FAT ファイル システムによって実行されるこれらのセマンティック セキュリティ チェックは、ハンドルを渡す操作に対してファイル システムが必要とする典型的なチェックです。 さらに、FAT ファイル システムでは、操作に固有のサニティ チェックも実行する必要があります。 これらのサニティ チェックは、呼び出し元が許可されていない特権操作を実行できないようにするために、異なるパラメーターに互換性がある (移動されるファイルが開いているボリューム上にあるなど) ことを確認するためのものです。
あらゆるファイル システムで、正しいセキュリティは次のようなファイル システムの制御操作に不可欠です。
ユーザー ハンドルの適切な検証。
ユーザー バッファー アクセスの保護。
特定の操作のセマンティクスの検証。
多くの場合、検証とセキュリティを正しく実行するために必要なコードが、特定の関数内のコードの大部分を占めます。