Compartilhar via


Verificando se há privilégios de passagem no IRP_MJ_CREATE

Uma das principais preocupações IRP_MJ_CREATE verifica se o chamador tem privilégio de passagem, que é o direito de acessar o caminho para um objeto. Ou seja, um chamador pode ter acesso a um objeto de arquivo como dirA/dirB/file, mas não tem permissão para acessar o conteúdo dos diretórios ao longo do caminho desse objeto de arquivo (dirA e dirA/dirB).

Por padrão, o Windows concede privilégio de passagem a todos os usuários. A constante "Direito do Usuário" é SeChangeNotifyPrivilege, que é mapeada para FILE_TRAVERSE em um ACCESS_MASK. Como um recurso de segurança, os administradores do sistema podem remover o privilégio de passagem de um usuário.

Como a maioria dos chamadores tem privilégio de passagem, uma das primeiras verificações que o sistema de arquivos normalmente faz é marcar para esse privilégio no campo AccessState-Flags> do contexto de segurança do IRP:

    BOOLEAN traverseCheck = 
        !(IrpContext->IrpSp->Parameters.Create.SecurityContext->AccessState->Flags
            & TOKEN_HAS_TRAVERSE_PRIVILEGE);

O sistema de arquivos usa Sinalizadores para rastrear quais direitos de acesso foram concedidos no andamento de uma operação. O sistema de arquivos pode marcar rapidamente os bits de estado de acesso primeiro e evitar a despesa de um acesso marcar chamada se o acesso já tiver sido concedido (traverseCheck = 0).

Se o privilégio de passagem não tiver sido concedido anteriormente, o sistema de arquivos deverá fazer uma passagem marcar em cada diretório ao longo do caminho para o arquivo que está sendo aberto. No snippet de código parcial abaixo, a marcar de passagem é feita usando uma rotina genérica, normalmente usada para a maioria das verificações de segurança:


{
// accessParams is passed to the file system and is normally based
// on the fields of the same name from the IRP.

// Only one thread can be looking at this data structure in memory
// at a time (and potentially changing it), so acquire a lock on it.

    SeLockSubjectContext(
        &accessParams.AccessState->SubjectSecurityContext);

// Check whether the desired access can be granted.
// For this example, assume desiredAccess = FILE_TRAVERSE

    granted = SeAccessCheck( Fcb->SecurityDescriptor,
        &AccessParams.AccessState->SubjectSecurityContext,
        TRUE,
        AccessParams.desiredAccess,
        0,
        &Privileges,
        IoGetFileObjectGenericMapping(),
        AccessParams.AccessMode,
        &AccessParams.GrantedAccess,
        &AccessParams.status );

    // The file system uses AccessState to cache access privileges
    // that have been granted thus far along the operation's code
    // path. Update AccessState with the newly acquired Privileges.
    
    if (Privileges != NULL) {

        (void) SeAppendPrivileges(AccessParams.AccessState, Privileges );
        SeFreePrivileges( Privileges );
        Privileges = NULL;
    }

    if (granted) {
        //
        // The desired access was granted, so clear the
        // granted bits from desiredAccess. 
        //
        AccessParams.desiredAccess &= 
            ~(AccessParams.GrantedAccess | MAXIMUM_ALLOWED);
 
        if (!checkOnly) {
        //
        // The caller wants to modify the access state for this 
        // request
        //
            AccessParams.AccessState->PreviouslyGrantedAccess |= 
                AccessParams.GrantedAccess;
        }

        if (maxDesired) {

            maxDelete = 
                (BOOLEAN)(AccessParams.AccessState->PreviouslyGrantedAccess & 
                    DELETE);
            maxReadAttr = 
                (BOOLEAN)(AccessParams.AccessState->PreviouslyGrantedAccess & 
                    FILE_READ_ATTRIBUTES);
        }
        AccessParams.AccessState->RemainingDesiredAccess &= 
            ~(AccessParams.GrantedAccess | MAXIMUM_ALLOWED);
    }

    // Release the lock on the security context
    SeUnlockSubjectContext(&accessParams.AccessState->SubjectSecurityContext);  
}

Essa função executa uma marcar de segurança genérica. Essa função deve lidar com os seguintes problemas ao fazer isso:

  • Ele deve especificar o descritor de segurança correto a ser usado para o marcar.

  • Ele deve passar o contexto de segurança (essas são as credenciais da entidade que está executando a operação).

  • Ele deve atualizar o estado de acesso com base nos resultados da marcar de segurança.

  • Ele deve considerar a opção MAXIMUM_ALLOWED (consulte ntifs.h). A opção MAXIMUM_ALLOWED especifica que o sistema de arquivos deve definir o acesso ao máximo possível de acesso permitido pelo sistema de arquivos (leitura/gravação/exclusão, por exemplo). Pouquíssimos aplicativos usam a opção MAXIMUM_ALLOWED porque essa opção não tem suporte no sistema de arquivos FASTFAT. Como o bit de opção MAXIMUM_ALLOWED não é um dos bits de acesso que o sistema de arquivos FASTFAT reconhece, ele rejeita solicitações de acesso ao arquivo determinado. Um aplicativo que tenta abrir um arquivo em um volume FASTFAT com o conjunto de opções MAXIMUM_ALLOWED descobrirá que a solicitação falha. Para obter detalhes, consulte a função FatCheckFileAccess no arquivo de origem Acchksup.c do código de exemplo FASTFAT que o WDK contém.

Observe que, para uma passagem simples marcar, o acesso solicitado seria FILE_TRAVERSE e o descritor de segurança seria o do diretório pelo qual o chamador está tentando atravessar, não o acesso solicitado do IRP IRP_MJ_CREATE original.