IRP_MJ_QUERY_SECURITY et IRP_MJ_SET_SECURITY
Heureusement pour un système de fichiers, le stockage et la récupération réels des descripteurs de sécurité sont relativement opaques. Cela est dû à la nature des descripteurs de sécurité dans un format auto-relatif qui ne nécessite aucune compréhension du descripteur par le système de fichiers. Ainsi, le traitement d’une opération de requête est normalement un exercice très simple. Voici un exemple d’implémentation de système de fichiers :
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;
}
Notez que cette routine s’appuie sur une fonction externe pour charger le descripteur de sécurité réel à partir d’un stockage persistant (dans cette implémentation, cette routine ne charge le descripteur de sécurité que s’il n’a pas été chargé précédemment). Étant donné que le descripteur de sécurité est opaque pour le système de fichiers, le moniteur de référence de sécurité doit être utilisé pour copier le descripteur dans la mémoire tampon de l’utilisateur. Nous notons deux points en ce qui concerne cet exemple de code :
La conversion du code d’erreur STATUS_BUFFER_TOO_SMALL en code d’avertissement STATUS_BUFFER_OVERFLOW est nécessaire pour fournir un comportement correct à certains outils de sécurité Windows.
Des erreurs dans la gestion de la mémoire tampon utilisateur peuvent et vont se produire, car les opérations de sécurité de requête et de définition sont normalement effectuées directement à l’aide de la mémoire tampon utilisateur. Notez que cela est contrôlé par le membre Flags du DEVICE_OBJECT créé par le système de fichiers. Dans une implémentation de système de fichiers basée sur ce code, la fonction appelante doit utiliser un bloc __try pour se protéger contre une mémoire tampon utilisateur non valide.
Les spécificités de la façon dont le système de fichiers charge un descripteur de sécurité à partir du stockage (la fonction FsdLoadSecurityDescriptor dans cet exemple) dépendent entièrement de l’implémentation du stockage de descripteur de sécurité dans le système de fichiers.
Le stockage d’un descripteur de sécurité est un peu plus impliqué. Les systèmes de fichiers peuvent avoir besoin de déterminer si le descripteur de sécurité correspond à un descripteur de sécurité existant si le système de fichiers prend en charge le partage de descripteur de sécurité. Pour les descripteurs de sécurité qui ne correspondent pas, le système de fichiers peut avoir besoin d’allouer un nouveau stockage pour ce nouveau descripteur de sécurité. Vous trouverez ci-dessous un exemple de routine pour remplacer le descripteur de sécurité sur un fichier.
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;
}
Notez qu’il s’agit d’un domaine dans lequel l’implémentation varie considérablement d’un système de fichiers à l’autre. Par exemple, un système de fichiers qui prend en charge le partage de descripteur de sécurité doit ajouter une logique explicite pour trouver un descripteur de sécurité correspondant. Cet exemple n’est qu’une tentative de fournir des conseils aux implémenteurs.