Condividi tramite


Verifica della presenza di privilegi di attraversamento in IRP_MJ_CREATE

Uno dei principali problemi IRP_MJ_CREATE verifica se il chiamante ha privilegi attraversati, ovvero il diritto di accedere al percorso a un oggetto. Ovvero, un chiamante può avere accesso a un oggetto file, ad esempio dirA/dirB/file, ma non dispone dell'autorizzazione per accedere al contenuto delle directory lungo il percorso dell'oggetto file (dirA e dirA/dirB).

Per impostazione predefinita, Windows concede l'attraversamento dei privilegi a tutti gli utenti. La costante "User Right" è SeChangeNotifyPrivilege, che esegue il mapping a FILE_TRAVERSE in un ACCESS_MASK. Come funzionalità di sicurezza, gli amministratori di sistema possono rimuovere i privilegi di attraversamento da un utente.

Poiché la maggior parte dei chiamanti ha il privilegio di attraversamento, una delle prime verifiche che il file system esegue normalmente è verificare la presenza di questo privilegio nel campo AccessState-Flags> del contesto di sicurezza di IRP:

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

Il file system usa flag per tenere traccia dei diritti di accesso concessi in corso di un'operazione. Il file system può quindi controllare rapidamente i bit di stato di accesso prima ed evitare la spesa di una chiamata di controllo di accesso se l'accesso è già stato concesso (traverseCheck = 0).

Se l'attraversamento dei privilegi non è stato concesso in precedenza, il file system deve eseguire un controllo di attraversamento in ogni directory lungo il percorso del file aperto. Nel frammento di codice parziale seguente, il controllo di attraversamento viene eseguito usando una routine generica, in genere usata per la maggior parte dei controlli di sicurezza:


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

Questa funzione esegue un controllo di sicurezza generico. Questa funzione deve gestire i seguenti problemi in questo modo:

  • Deve specificare il descrittore di sicurezza corretto da usare per il controllo.

  • Deve passare lungo il contesto di sicurezza (queste sono le credenziali dell'entità che esegue l'operazione).

  • Deve aggiornare lo stato di accesso in base ai risultati del controllo di sicurezza.

  • Deve tenere conto dell'opzione MAXIMUM_ALLOWED (vedere ntifs.h). L'opzione MAXIMUM_ALLOWED specifica che il file system deve impostare l'accesso al massimo possibile consentito dal file system (lettura/scrittura/eliminazione, ad esempio). Poche applicazioni usano l'opzione MAXIMUM_ALLOWED perché questa opzione non è supportata nel file system FASTFAT. Poiché il bit dell'opzione MAXIMUM_ALLOWED non è uno dei bit di accesso riconosciuti dal file system FASTFAT, rifiuta le richieste di accesso al file specificato. Un'applicazione che tenta di aprire un file in un volume FASTFAT con il set di opzioni MAXIMUM_ALLOWED individua che la richiesta ha esito negativo. Per informazioni dettagliate, vedere la funzione FatCheckFileAccess nel file di origine Acchksup.c del codice di esempio FASTFAT che il WDK contiene.

Si noti che per un semplice controllo di attraversamento, l'accesso richiesto sarebbe FILE_TRAVERSE e il descrittore di sicurezza sarebbe quello della directory tramite cui il chiamante sta tentando di attraversare, non l'accesso richiesto dall'IRP_MJ_CREATE IRP originale.