Compartir a través de


Comprobación de privilegios de recorrido en IRP_MJ_CREATE

Una de las principales preocupaciones IRP_MJ_CREATE comprueba si el autor de la llamada tiene privilegios de recorrido, que es el derecho a acceder a la ruta de acceso a un objeto. Es decir, un llamador puede tener acceso a un objeto de archivo como dirA/dirB/file, pero no tener permiso para acceder al contenido de los directorios a lo largo de la ruta de acceso del objeto de archivo (dirA y dirA/dirB).

De forma predeterminada, Windows concede privilegios de recorrido a todos los usuarios. La constante "User Right" es SeChangeNotifyPrivilege, que se asigna a FILE_TRAVERSE en un ACCESS_MASK. Como característica de seguridad, los administradores del sistema pueden quitar privilegios de recorrido de un usuario.

Dado que la mayoría de los autores de llamadas tienen privilegios de recorrido, una de las primeras comprobaciones que normalmente hace el sistema de archivos es comprobar este privilegio en el campo AccessState-Flags> del contexto de seguridad de IRP:

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

El sistema de archivos usa marcas para realizar un seguimiento de los derechos de acceso que se han concedido en el progreso de una operación. El sistema de archivos puede comprobar rápidamente primero los bits de estado de acceso y evitar el gasto de una llamada de comprobación de acceso si ya se ha concedido acceso (traverseCheck = 0).

Si no se ha concedido previamente privilegios de recorrido, el sistema de archivos debe realizar una comprobación de recorrido en cada directorio a lo largo de la ruta de acceso al archivo que se abre. En el fragmento de código parcial siguiente, la comprobación de recorrido se realiza mediante una rutina genérica, que normalmente se usa para la mayoría de las comprobaciones de seguridad:


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

Esta función realiza una comprobación de seguridad genérica. Esta función debe tratar los siguientes problemas al hacerlo:

  • Debe especificar el descriptor de seguridad correcto que se usará para la comprobación.

  • Debe pasar el contexto de seguridad (estas son las credenciales de la entidad que realiza la operación).

  • Debe actualizar el estado de acceso en función de los resultados de la comprobación de seguridad.

  • Debe tener en cuenta la opción MAXIMUM_ALLOWED (consulte ntifs.h). La opción MAXIMUM_ALLOWED especifica que el sistema de archivos debe establecer el acceso al máximo posible acceso permitido por el sistema de archivos (lectura, escritura y eliminación, por ejemplo). Muy pocas aplicaciones usan la opción MAXIMUM_ALLOWED porque esta opción no es compatible con el sistema de archivos FASTFAT. Dado que el bit de opción MAXIMUM_ALLOWED no es uno de los bits de acceso que reconoce el sistema de archivos FASTFAT, rechaza las solicitudes de acceso al archivo especificado. Una aplicación que intenta abrir un archivo en un volumen FASTFAT con el conjunto de opciones MAXIMUM_ALLOWED encontrará que se produce un error en la solicitud. Para obtener más información, vea la función FatCheckFileAccess en el archivo de código fuente Acchksup.c del código de ejemplo FASTFAT que contiene el WDK.

Tenga en cuenta que, para una comprobación de recorrido simple, el acceso solicitado sería FILE_TRAVERSE y el descriptor de seguridad sería el del directorio a través del cual el autor de la llamada está intentando recorrer, no el acceso solicitado desde el IRP de IRP_MJ_CREATE original.