次の方法で共有


IRP_MJ_CREATE での走査特権の確認

IRP_MJ_CREATE のチェックで主に懸念されることの 1 つは、オブジェクトへのパスにアクセスする権限である走査特権を呼び出し元が持っているかどうかです。 つまり、呼び出し元は、dirA/dirB/file のようなファイル オブジェクトにはアクセスできても、そのファイル オブジェクトのパスに至るまでのディレクトリ (dirAdirA/dirB) の内容にアクセスするためのアクセス許可を持っていない可能性があります。

既定では、Windows はすべてのユーザーに走査特権を付与します。 "ユーザー権限" 定数は SeChangeNotifyPrivilege であり、ACCESS_MASKFILE_TRAVERSE にマップされます。 セキュリティ機能として、システム管理者はユーザーから走査特権を削除できます。

ほとんどの呼び出し元は走査特権を持っているため、ファイル システムで通常最初に行われるチェックの 1 つは、IRP のセキュリティ コンテキストの AccessState->Flags フィールドでこの特権をチェックすることです。

    BOOLEAN traverseCheck = 
        !(IrpContext->IrpSp->Parameters.Create.SecurityContext->AccessState->Flags
            & TOKEN_HAS_TRAVERSE_PRIVILEGE);

ファイル システムは、Flags を使って、操作の進行中に付与されたアクセス権を追跡します。 このようにすると、ファイル システムはアクセス状態ビットを最初にチェックするだけで済み、アクセスが既に許可されている場合は、アクセス チェックを呼び出すコストを回避できます (traverseCheck = 0)。

それまでにスキャン特権が付与されていない場合、ファイル システムは、開いているファイルまでのパス上にある各ディレクトリで走査チェックを実行する必要があります。 以下の部分コード スニペットでは、ほとんどのセキュリティ チェックに通常使われる汎用ルーチンを使って、走査チェックが行われています。


{
// accessParams is passed to the file system and is normally based
// on the fields of the same name from the IRP.

// Only one thread can be looking at this data structure in memory
// at a time (and potentially changing it), so acquire a lock on it.

    SeLockSubjectContext(
        &accessParams.AccessState->SubjectSecurityContext);

// Check whether the desired access can be granted.
// For this example, assume desiredAccess = FILE_TRAVERSE

    granted = SeAccessCheck( Fcb->SecurityDescriptor,
        &AccessParams.AccessState->SubjectSecurityContext,
        TRUE,
        AccessParams.desiredAccess,
        0,
        &Privileges,
        IoGetFileObjectGenericMapping(),
        AccessParams.AccessMode,
        &AccessParams.GrantedAccess,
        &AccessParams.status );

    // The file system uses AccessState to cache access privileges
    // that have been granted thus far along the operation's code
    // path. Update AccessState with the newly acquired Privileges.
    
    if (Privileges != NULL) {

        (void) SeAppendPrivileges(AccessParams.AccessState, Privileges );
        SeFreePrivileges( Privileges );
        Privileges = NULL;
    }

    if (granted) {
        //
        // The desired access was granted, so clear the
        // granted bits from desiredAccess. 
        //
        AccessParams.desiredAccess &= 
            ~(AccessParams.GrantedAccess | MAXIMUM_ALLOWED);
 
        if (!checkOnly) {
        //
        // The caller wants to modify the access state for this 
        // request
        //
            AccessParams.AccessState->PreviouslyGrantedAccess |= 
                AccessParams.GrantedAccess;
        }

        if (maxDesired) {

            maxDelete = 
                (BOOLEAN)(AccessParams.AccessState->PreviouslyGrantedAccess & 
                    DELETE);
            maxReadAttr = 
                (BOOLEAN)(AccessParams.AccessState->PreviouslyGrantedAccess & 
                    FILE_READ_ATTRIBUTES);
        }
        AccessParams.AccessState->RemainingDesiredAccess &= 
            ~(AccessParams.GrantedAccess | MAXIMUM_ALLOWED);
    }

    // Release the lock on the security context
    SeUnlockSubjectContext(&accessParams.AccessState->SubjectSecurityContext);  
}

この関数は、汎用セキュリティ チェックを実行します。 この関数では、そのために次の問題に対処する必要があります。

  • チェックに使う適切なセキュリティ記述子を指定する必要があります。

  • セキュリティ コンテキストを渡す必要があります (これらは、操作を実行しているエンティティの資格情報です)。

  • セキュリティ チェックの結果に基づいてアクセス状態を更新する必要があります。

  • MAXIMUM_ALLOWED オプションを考慮する必要があります (ntifs.h を参照)。 MAXIMUM_ALLOWED オプションは、ファイル システムによって許可される最大限可能なアクセスに、ファイル システムがアクセスを設定する必要があることを指定します (読み取り/書き込み/削除など)。 MAXIMUM_ALLOWED オプションは FASTFAT ファイル システムではサポートされていないため、このオプションを使うアプリケーションはほとんどありません。 FASTFAT ファイル システムは、アクセス ビットの 1 つとして MAXIMUM_ALLOWED オプション ビットを認識しないため、指定されたファイルへのアクセス要求を拒否します。 アプリケーションで、MAXIMUM_ALLOWED オプションを設定して FASTFAT ボリューム上のファイルを開こうとしても、要求は失敗します。 詳細については、WDK に含まれる FASTFAT サンプル コードの Acchksup.c ソース ファイルの FatCheckFileAccess 関数を参照してください。

簡単な走査チェックのため、元の IRP_MJ_CREATE IRP で要求されたアクセスではなく、要求されるアクセスは FILE_TRAVERSE であり、セキュリティ記述子は呼び出し元が走査を試みているディレクトリのものであることに注意してください。