Проверка привилегий traverse в IRP_MJ_CREATE

Одной из основных проблем, IRP_MJ_CREATE проверок, является то, имеет ли вызывающий объект права доступа к пути к объекту. То есть вызывающий объект может иметь доступ к объекту файла, например dirA/dirB/file, но не иметь разрешения на доступ к содержимому каталогов по пути к объекту файла (dirA и dirA/dirB).

По умолчанию Windows предоставляет права прохода всем пользователям. Константой "Право пользователя" является SeChangeNotifyPrivilege, которая сопоставляется с FILE_TRAVERSE в ACCESS_MASK. В качестве функции безопасности системные администраторы могут удалить привилегии обхода у пользователя.

Так как большинство вызывающих объектов имеют привилегию обхода, одной из первых проверок, которую обычно выполняет файловая система, является проверка для этой привилегии в поле AccessState-Flags> контекста безопасности IRP:

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

Файловая система использует флаги для отслеживания того, какие права доступа были предоставлены в ходе выполнения операции. Затем файловая система может просто быстро проверка биты состояния доступа и избежать расходов на доступ проверка вызова, если доступ уже предоставлен (traverseCheck = 0).

Если разрешение обхода ранее не было предоставлено, файловая система должна выполнить обход проверка по каждому каталогу по пути к открываемой папке. В приведенном ниже фрагменте кода обход проверка выполняется с помощью универсальной подпрограммы, обычно используемой для большинства проверок безопасности:


{
// 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);  
}

Эта функция выполняет универсальную проверка безопасности. При этом эта функция должна решать следующие проблемы:

  • Он должен указать правильный дескриптор безопасности, используемый для проверка.

  • Он должен передавать контекст безопасности (это учетные данные сущности, выполняющей операцию).

  • Он должен обновить состояние доступа на основе результатов проверка безопасности.

  • Он должен учитывать параметр MAXIMUM_ALLOWED (см. ntifs.h). Параметр MAXIMUM_ALLOWED указывает, что файловая система должна установить для доступа максимально возможный доступ, разрешенный файловой системой (например, для чтения, записи и удаления). Очень немногие приложения используют параметр MAXIMUM_ALLOWED, так как этот параметр не поддерживается в файловой системе FASTFAT. Так как бит параметра MAXIMUM_ALLOWED не является одним из битов доступа, распознаемых файловой системой FASTFAT, он отклоняет запросы на доступ к указанному файлу. Приложение, которое пытается открыть файл на томе FASTFAT с набором параметров MAXIMUM_ALLOWED, обнаружит, что запрос завершится ошибкой. Дополнительные сведения см. в функции FatCheckFileAccess в исходном файле Acchksup.c примера кода FASTFAT, который содержит WDK.

Обратите внимание, что для простого обхода проверка запрошенный доступ будет FILE_TRAVERSE, а дескриптором безопасности будет каталог, через который вызывающий объект пытается пройти, а не запрошенный доступ из исходного IRP_MJ_CREATE IRP.