Partager via


Vérification des privilèges de traverse sur IRP_MJ_CREATE

L’une des principales préoccupations IRP_MJ_CREATE les vérifications est de savoir si l’appelant dispose d’un privilège de traversée, qui est le droit d’accéder au chemin d’accès à un objet. Autrement dit, un appelant peut avoir accès à un objet de fichier tel que dirA/dirB/file, mais n’a pas l’autorisation d’accéder au contenu des répertoires le long du chemin d’accès de cet objet de fichier (dirA et dirA/dirB).

Par défaut, Windows accorde des privilèges de traversée à tous les utilisateurs. La constante « Droit de l’utilisateur » est SeChangeNotifyPrivilege, qui correspond à FILE_TRAVERSE dans un ACCESS_MASK. En tant que fonctionnalité de sécurité, les administrateurs système peuvent supprimer les privilèges traversants d’un utilisateur.

Étant donné que la plupart des appelants disposent d’un privilège de passage, l’une des premières vérifications effectuées par le système de fichiers consiste à case activée pour ce privilège dans le champ AccessState-Flags> du contexte de sécurité de l’IRP :

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

Le système de fichiers utilise des indicateurs pour suivre les droits d’accès qui ont été accordés au cours d’une opération. Le système de fichiers peut alors rapidement case activée les bits d’état d’accès en premier, et éviter les frais d’un appel d’accès case activée si l’accès a déjà été accordé (traverseCheck = 0).

Si le privilège de traversée n’a pas été accordé précédemment, le système de fichiers doit effectuer une case activée de traversée sur chaque répertoire le long du chemin d’accès au fichier en cours d’ouverture. Dans l’extrait de code partiel ci-dessous, la traversée case activée est effectuée à l’aide d’une routine générique, généralement utilisée pour la plupart des vérifications de sécurité :


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

Cette fonction effectue une case activée de sécurité générique. Cette fonction doit résoudre les problèmes suivants :

  • Il doit spécifier le descripteur de sécurité approprié à utiliser pour le case activée.

  • Il doit transmettre le contexte de sécurité (il s’agit des informations d’identification de l’entité qui effectue l’opération).

  • Il doit mettre à jour l’état d’accès en fonction des résultats de la case activée de sécurité.

  • Il doit tenir compte de l’option MAXIMUM_ALLOWED (voir ntifs.h). L’option MAXIMUM_ALLOWED spécifie que le système de fichiers doit définir l’accès au maximum autorisé par le système de fichiers (lecture/écriture/suppression, par exemple). Très peu d’applications utilisent l’option MAXIMUM_ALLOWED, car cette option n’est pas prise en charge sur le système de fichiers FASTFAT. Étant donné que le bit d’option MAXIMUM_ALLOWED ne fait pas partie des bits d’accès que le système de fichiers FASTFAT reconnaît, il rejette les demandes d’accès au fichier donné. Une application qui tente d’ouvrir un fichier sur un volume FASTFAT avec l’option MAXIMUM_ALLOWED définie constate que la demande échoue. Pour plus d’informations, consultez la fonction FatCheckFileAccess dans le fichier source Acchksup.c de l’exemple de code FASTFAT que contient le WDK.

Notez que pour une simple traversée case activée, l’accès demandé serait FILE_TRAVERSE et le descripteur de sécurité serait celui du répertoire par lequel l’appelant tente de parcourir, et non de l’accès demandé à partir du IRP_MJ_CREATE IRP d’origine.