Share via


IRP_MJ_QUERY_SECURITY und IRP_MJ_SET_SECURITY

Glücklicherweise ist das tatsächliche Speichern und Abrufen von Sicherheitsbeschreibungen für ein Dateisystem relativ undurchsichtig. Dies ist auf die Art von Sicherheitsdeskriptoren in einem selbstrelativen Format zurückzuführen, das kein Verständnis des Deskriptors durch das Dateisystem erfordert. Daher ist die Verarbeitung eines Abfragevorgangs normalerweise eine sehr einfache Übung. Hier sehen Sie ein Beispiel aus einer Dateisystemimplementierung:

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;
}

Beachten Sie, dass diese Routine auf einer externen Funktion basiert, um den eigentlichen Sicherheitsdeskriptor aus persistentem Speicher zu laden (in dieser Implementierung lädt diese Routine den Sicherheitsdeskriptor nur, wenn er zuvor nicht geladen wurde). Da der Sicherheitsdeskriptor für das Dateisystem undurchsichtig ist, muss der Sicherheitsverweismonitor verwendet werden, um den Deskriptor in den Puffer des Benutzers zu kopieren. In Bezug auf dieses Codebeispiel sind zwei Punkte zu beachten:

  1. Die Konvertierung des Fehlercodes STATUS_BUFFER_TOO_SMALL in den Warnungscode STATUS_BUFFER_OVERFLOW ist erforderlich, um für einige Windows-Sicherheitstools ein korrektes Verhalten bereitzustellen.

  2. Fehler bei der Behandlung des Benutzerpuffers können und werden auftreten, da sowohl Abfrage- als auch Setsicherheitsvorgänge normalerweise direkt mit dem Benutzerpuffer ausgeführt werden. Beachten Sie, dass dies vom Flags-Member des vom Dateisystem erstellten DEVICE_OBJECT gesteuert wird. In einer Dateisystemimplementierung, die auf diesem Code basiert, muss die aufrufende Funktion einen __try-Block verwenden, um sich vor einem ungültigen Benutzerpuffer zu schützen.

Wie genau das Dateisystem einen Sicherheitsdeskriptor aus dem Speicher lädt (die FsdLoadSecurityDescriptor-Funktion in diesem Beispiel), hängt vollständig von der Implementierung des Sicherheitsdeskriptorspeichers im Dateisystem ab.

Das Speichern einer Sicherheitsbeschreibung ist etwas wichtiger. Dateisysteme müssen möglicherweise ermitteln, ob der Sicherheitsdeskriptor mit einem vorhandenen Sicherheitsdeskriptor übereinstimmt, wenn das Dateisystem die Freigabe von Sicherheitsbeschreibungen unterstützt. Bei nicht übereinstimmenden Sicherheitsbeschreibungen muss das Dateisystem möglicherweise neuen Speicher für diesen neuen Sicherheitsdeskriptor zuweisen. Im Folgenden finden Sie eine Beispielroutine zum Ersetzen des Sicherheitsdeskriptors für eine Datei.

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;
}

Beachten Sie, dass dies ein Bereich ist, in dem die Implementierung von Dateisystem zu Dateisystem erheblich variiert. Beispielsweise muss ein Dateisystem, das die Freigabe von Sicherheitsdeskriptoren unterstützt, eine explizite Logik hinzufügen, um einen übereinstimmenden Sicherheitsdeskriptor zu finden. Dieses Beispiel ist nur ein Versuch, Implementierern Anleitungen zu geben.