Поделиться через


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

Обратите внимание, что эта подпрограмма использует внешнюю функцию для загрузки фактического дескриптора безопасности из постоянного хранилища (в этой реализации эта подпрограмма загружает дескриптор безопасности только в том случае, если он не был загружен ранее). Так как дескриптор безопасности непрозрачн для файловой системы, для копирования дескриптора в буфер пользователя необходимо использовать монитор ссылок на безопасность. Мы заметим два момента в отношении этого примера кода:

  1. Преобразование кода ошибки STATUS_BUFFER_TOO_SMALL в код предупреждения STATUS_BUFFER_OVERFLOW необходимо для обеспечения правильного поведения некоторых средств безопасности Windows.

  2. Ошибки при обработке пользовательского буфера могут и будут возникать из-за того, что операции безопасности запроса и задания обычно выполняются с помощью буфера пользователя напрямую. Обратите внимание, что этим управляет элемент Flags 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;
}

Обратите внимание, что это область, в которой реализация значительно отличается от файловой системы к файловой системе. Например, файловой системе, поддерживающей общий доступ к дескрипторов безопасности, потребуется добавить явную логику для поиска соответствующего дескриптора безопасности. Этот пример является лишь попыткой предоставить рекомендации для разработчиков.