IRP_MJ_QUERY_SECURITY e IRP_MJ_SET_SECURITY
Fortunatamente per un file system, l'archiviazione effettiva e il recupero dei descrittori di sicurezza è relativamente opaco. Ciò è dovuto alla natura dei descrittori di sicurezza in un formato auto-relativo che non richiede alcuna comprensione del descrittore dal file system. Di conseguenza, l'elaborazione di un'operazione di query è normalmente un esercizio molto semplice. Ecco un esempio di implementazione di file system:
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;
}
Si noti che questa routine si basa su una funzione esterna per caricare il descrittore di sicurezza effettivo dall'archiviazione persistente (in questa implementazione, questa routine carica solo il descrittore di sicurezza se non è stato caricato in precedenza). Poiché il descrittore di sicurezza è opaco al file system, il monitoraggio dei riferimenti alla sicurezza deve essere usato per copiare il descrittore nel buffer dell'utente. Si notino due punti rispetto a questo esempio di codice:
La conversione del codice di errore STATUS_BUFFER_TOO_SMALL nel codice di avviso STATUS_BUFFER_OVERFLOW è necessaria per fornire un comportamento corretto per alcuni strumenti di sicurezza di Windows.
Gli errori nella gestione del buffer utente possono e si verificheranno perché le operazioni di sicurezza di query e set vengono normalmente eseguite usando direttamente il buffer utente. Si noti che questo è controllato dal membro Flags del DEVICE_OBJECT creato dal file system. In un'implementazione del file system basata su questo codice, la funzione chiamante deve usare un blocco __try per proteggere da un buffer utente non valido.
Le specifiche del modo in cui il file system carica un descrittore di sicurezza dall'archiviazione (la funzione FsdLoadSecurityDescriptor in questo esempio) dipenderà interamente dall'implementazione dell'archiviazione descrittore di sicurezza nel file system.
L'archiviazione di un descrittore di sicurezza è un po' più coinvolto. I file system potrebbero dover determinare se il descrittore di sicurezza corrisponde a un descrittore di sicurezza esistente se il file system supporta la condivisione del descrittore di sicurezza. Per i descrittori di sicurezza non corrispondenti, il file system potrebbe dover allocare nuova risorsa di archiviazione per questo nuovo descrittore di sicurezza. Di seguito è riportata una routine di esempio per sostituire il descrittore di sicurezza in un file.
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;
}
Si noti che si tratta di un'area in cui l'implementazione varia notevolmente da file system a file system. Ad esempio, un file system che supporta la condivisione del descrittore di sicurezza deve aggiungere logica esplicita per trovare un descrittore di sicurezza corrispondente. Questo esempio è solo un tentativo di fornire indicazioni per gli implementatori.