Partilhar via


IRP_MJ_QUERY_SECURITY e IRP_MJ_SET_SECURITY

Felizmente para um sistema de arquivos, o armazenamento real e a recuperação de descritores de segurança são relativamente opacos. Isso ocorre devido à natureza dos descritores de segurança em um formato auto-relativo que não exige nenhuma compreensão do descritor pelo sistema de arquivos. Portanto, o processamento de uma operação de consulta normalmente é um exercício muito simples. Aqui está um exemplo de uma implementação do sistema de arquivos:

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

Observe que essa rotina depende de uma função externa para carregar o descritor de segurança real do armazenamento persistente (nessa implementação, essa rotina só carregará o descritor de segurança se ele não tiver sido carregado anteriormente). Como o descritor de segurança é opaco para o sistema de arquivos, o monitor de referência de segurança deve ser usado para copiar o descritor para o buffer do usuário. Observamos dois pontos em relação a este exemplo de código:

  1. A conversão do código de erro STATUS_BUFFER_TOO_SMALL no código de aviso STATUS_BUFFER_OVERFLOW é necessária para fornecer o comportamento correto para algumas ferramentas de segurança do Windows.

  2. Erros ao lidar com o buffer de usuário podem e surgirão porque as operações de segurança de consulta e de definição normalmente são feitas usando o buffer do usuário diretamente. Observe que isso é controlado pelo membro Flags do DEVICE_OBJECT criado pelo sistema de arquivos. Em uma implementação do sistema de arquivos com base nesse código, a função de chamada precisaria usar um bloco __try para proteger contra um buffer de usuário inválido.

As especificidades de como o sistema de arquivos carrega um descritor de segurança do armazenamento (a função FsdLoadSecurityDescriptor neste exemplo) dependerão inteiramente da implementação do armazenamento do descritor de segurança no sistema de arquivos.

Armazenar um descritor de segurança é um pouco mais envolvido. Os sistemas de arquivos podem precisar determinar se o descritor de segurança corresponde a um descritor de segurança existente se o sistema de arquivos der suporte ao compartilhamento de descritor de segurança. Para descritores de segurança não correspondentes, o sistema de arquivos pode precisar alocar um novo armazenamento para esse novo descritor de segurança. Abaixo está um exemplo de rotina para substituir o descritor de segurança em um arquivo.

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

Observe que essa é uma área na qual a implementação varia drasticamente de sistema de arquivos para sistema de arquivos. Por exemplo, um sistema de arquivos que dá suporte ao compartilhamento de descritor de segurança precisaria adicionar lógica explícita para encontrar um descritor de segurança correspondente. Este exemplo é apenas uma tentativa de fornecer diretrizes aos implementadores.