幸いなことに、ファイル システムの場合、セキュリティ記述子の実際のストレージと取得は比較的不透明です。 これは、ファイル システムによる記述子の理解を必要としない自己相対形式のセキュリティ記述子の性質によるものです。 したがって、クエリ操作の処理は通常、非常に簡単な演習です。 ファイル システムの実装の例を次に示します。
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 つの点に注意してください。
エラー コードSTATUS_BUFFER_TOO_SMALL警告コード STATUS_BUFFER_OVERFLOWへの変換は、一部の Windows セキュリティ ツールに正しい動作を提供するために必要です。
クエリと設定の両方のセキュリティ操作は通常、ユーザー バッファーを直接使用して実行されるため、ユーザー バッファーの処理中にエラーが発生する可能性があります。また、エラーが発生する可能性があります。 これは、ファイル システムによって作成されたDEVICE_OBJECTの Flags メンバーによって制御されることに注意してください。 このコードに基づくファイル システムの実装では、呼び出し元の関数は、無効なユーザー バッファーから保護するために__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;
}
これは、実装がファイル システムによって大きく異なる領域であることに注意してください。 たとえば、セキュリティ記述子の共有をサポートするファイル システムでは、一致するセキュリティ記述子を見つけるために明示的なロジックを追加する必要があります。 このサンプルは、実装者にガイダンスを提供するための試みにすぎません。