次の方法で共有


IRP_MJ_QUERY_SECURITY と IRP_MJ_SET_SECURITY

ファイル システムにとって幸いなことに、セキュリティ記述子の実際の保存と取得は比較的不透明です。 これは、ファイル システムが記述子を理解する必要がない自己相対形式のセキュリティ記述子の性質によるものです。 したがって、クエリ操作の処理は、通常、非常に単純な作業です。 ファイル システム実装の例を次に示します。

NTSTATUS FsdCommonQuerySecurity( PIRP_CONTEXT IrpContext)
{
    NTSTATUS status = STATUS_SUCCESS;
    PSECURITY_DESCRIPTOR LocalPointer;

    // Need to add code to lock the FCB here

    status = FsdLoadSecurityDescriptor(IrpContext, IrpContext->Fcb);
 
    if (NT_SUCCESS(status) ) {
 
        //
        // copy the SecurityDescriptor into the callers buffer
        // note that this copy can throw an exception that must be handled
        // (code to handle the exception was omitted here for brevity)
        //
        LocalPointer = IrpContext->Fcb->SecurityDescriptor;

        status = SeQuerySecurityDescriptorInfo(
     &IrpContext->IrpSp->Parameters.QuerySecurity.SecurityInformation,
            (PSECURITY_DESCRIPTOR)IrpContext->Irp->UserBuffer,
            &IrpContext->IrpSp->Parameters.QuerySecurity.Length,
            &LocalPointer );
 
        //
        // CACLS utility expects OVERFLOW
        //
        if (status == STATUS_BUFFER_TOO_SMALL ) {
            status = STATUS_BUFFER_OVERFLOW;
        }
    }
 
    // Need to add code to unlock the FCB here

    return status;
}

このルーチンは外部関数に依存して実際のセキュリティ記述子を永続ストレージからロードすることに注意してください (この実装では、そのルーチンは、セキュリティ記述子が以前にロードされていない場合にのみセキュリティ記述子をロードします)。 セキュリティ記述子はファイル システムに対して不透明であるため、セキュリティ参照モニタを使用して記述子をユーザーのバッファにコピーする必要があります。 このコードサンプルに関しては、次の 2 つの点に注意してください。

  1. 一部の Windows セキュリティ ツールに正しい動作を提供するには、エラー コード STATUS_BUFFER_TOO_SMALL を警告コード STATUS_BUFFER_OVERFLOW に変換する必要があります。

  2. ユーザー バッファーの処理でエラーが発生する可能性があり、実際に発生するのは、クエリ操作とセキュリティ設定操作の両方が通常、ユーザー バッファーを直接使用して行われるためです。 注: これは、ファイル システムによって作成されたDEVICE_OBJECTのフラグメンバーによって制御されます。 このコードに基づくファイル システム実装では、呼び出し側関数は、無効なユーザー バッファーから保護するために __try ブロックを使用する必要があります。

ファイル システムがストレージからセキュリティ記述子をロードする方法の詳細 (この例FsdLoadSecurityDescriptor の関数) は、ファイルシステム内のセキュリティ記述子ストレージの実装に完全に依存します。

セキュリティ記述子の保存はもう少し複雑です。 ファイル システムがセキュリティ記述子の共有をサポートしている場合、ファイル システムは、セキュリティ記述子が既存のセキュリティ記述子と一致するかどうかを判断する必要がある場合があります。 セキュリティ記述子が一致しない場合、ファイル システムは、この新しいセキュリティ記述子に新しいストレージを割り当てる必要がある場合があります。 以下は、ファイルのセキュリティ記述子を置き換えるサンプル ルーチンです。

NTSTATUS FsdCommonSetSecurity(PIRP_CONTEXT IrpContext)
{
    NTSTATUS status = STATUS_SUCCESS;
    PSECURITY_DESCRIPTOR SavedDescriptorPtr = 
        IrpContext->Fcb->SecurityDescriptor;
    ULONG SavedDescriptorLength = 
        IrpContext->Fcb->SecurityDescriptorLength;
    PSECURITY_DESCRIPTOR newSD = NULL;
    POW_FCB Fcb = IrpContext->Fcb;
    ULONG Information = IrpContext->Irp->IoStatus.Information;

    //
    // make sure that the FCB security descriptor is up to date
    //
    status = FsdLoadSecurityDescriptor(IrpContext, Fcb);

    if (!NT_SUCCESS(status)) {
      //
      // Something is seriously wrong 
      //
      IrpContext->Irp->IoStatus.Status = status;
      IrpContext->Irp->IoStatus.Information = 0;
      return status;
    }        
 
    status = SeSetSecurityDescriptorInfo(
       NULL,
       &IrpContext->IrpSp->Parameters.SetSecurity.SecurityInformation,
       IrpContext->IrpSp->Parameters.SetSecurity.SecurityDescriptor,
       &Fcb->SecurityDescriptor,
       PagedPool,
       IoGetFileObjectGenericMapping()
       );

    if (!NT_SUCCESS(status)) {

        //
        // restore things  and return
        //
        Fcb->SecurityDescriptorLength = SavedDescriptorLength;
        Fcb->SecurityDescriptor = SavedDescriptorPtr;
        IrpContext->Irp->IoStatus.Status = status;
        IrpContext->Irp->IoStatus.Information = 0;

        return status;
    }

    //
    // get the new length
    //
    Fcb->SecurityDescriptorLength = 
        RtlLengthSecurityDescriptor(Fcb->SecurityDescriptor);

    //
    // allocate our own private SD to replace the one from
    // SeSetSecurityDescriptorInfo so we can track our memory usage
    //
    newSD = ExAllocatePoolWithTag(PagedPool, 
        Fcb->SecurityDescriptorLength, 'DSyM');

    if (!newSD) {
 
      //
      // paged pool is empty
      //
      SeDeassignSecurity(&Fcb->SecurityDescriptor);
      status = STATUS_NO_MEMORY;
      Fcb->SecurityDescriptorLength = SavedDescriptorLength;
      Fcb->SecurityDescriptor = SavedDescriptorPtr;
 
      //
      // make sure FCB security is in a valid state
      //
      IrpContext->Irp->IoStatus.Status = status;
      IrpContext->Irp->IoStatus.Information = 0;
 
      return status;
 
    } 
 
    //
    // store the new security on disk
    //
    status = FsdStoreSecurityDescriptor(IrpContext, Fcb);

    if (!NT_SUCCESS(status)) {
      //
      // great- modified the in-core SD but couldn't get it out
      // to disk. undo everything. 
      //
      ExFreePool(newSD);
      SeDeassignSecurity(&Fcb->SecurityDescriptor);
      status = STATUS_NO_MEMORY;
      Fcb->SecurityDescriptorLength = SavedDescriptorLength;
      Fcb->SecurityDescriptor = SavedDescriptorPtr;
      IrpContext->Irp->IoStatus.Status = status;
      IrpContext->Irp->IoStatus.Information = 0;
 
      return status;
    }
 
    //
    // if we get here everything worked! 
    //
    RtlCopyMemory(newSD, Fcb->SecurityDescriptor, 
        Fcb->SecurityDescriptorLength);
 
    //
    // deallocate the security descriptor
    //
    SeDeassignSecurity(&Fcb->SecurityDescriptor);
 
    //
    // this either is the new private SD or NULL if 
    // memory allocation failed
    //
    Fcb->SecurityDescriptor = newSD;

    //
    // free the memory from the previous descriptor
    //
    if (SavedDescriptorPtr) {
      //
      // this  must always be from private allocation
      //
      ExFreePool(SavedDescriptorPtr);
 
    }        
 
    IrpContext->Irp.IoStatus = status;
    IrpContext->Irp.Information = Information;

    return status;
}

これは、実装がファイル システムによって大きく異なる領域であることに注意してください。 たとえば、セキュリティ記述子の共有をサポートするファイル システムでは、一致するセキュリティ記述子を見つけるために明示的なロジックを追加する必要があります。 このサンプルは、実装者にガイダンスを提供することを目的としたものにすぎません。