2010-04-20 4 views
8

Utilizziamo l'autenticazione NTLM nella nostra applicazione per determinare se un utente può eseguire determinate operazioni. Usiamo il IPrincipal del loro attuale login di Windows (nelle applicazioni WinForms), chiamando IsInRole per verificare l'appartenenza a un gruppo specifico.Chiamare IPrincipal.IsInRole su Windows 7

Per controllare che un utente è un amministratore locale sulla macchina, usiamo:

AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); 
... 
bool allowed = Thread.CurrentPrincipal.IsInRole(@"Builtin\Administrators") 

Questo funziona se l'utente corrente è l'utente Administrator, o è un altro utente che è un membro del gruppo Builtin\Administrators .

Nei nostri test su Windows 7, abbiamo rilevato che questo non funziona più come previsto. L'utente Administrator funziona ancora correttamente, ma qualsiasi altro utente membro del gruppo Builtin\Administrators restituisce false per la chiamata IsInRole.

Che cosa potrebbe causare questa differenza? Ho la sensazione che un'impostazione predefinita sia cambiata da qualche parte (possibile in gpedit), ma non riesce a trovare nulla che assomigli al colpevole.

+0

Dopo aver posto questa domanda, ho avuto un problema con un codice che controllava manualmente l'appartenenza al gruppo e replicava il comportamento su una macchina Vista. Questo mi ha portato all'idea che il problema del controllo dell'account utente era il problema: la maggior parte delle macchine che eseguono Vista qui hanno disattivato il controllo dell'account utente. L'applicazione controlla l'appartenenza ai gruppi definiti da un'altra origine, quindi può variare a seconda che l'applicazione debba essere eseguita come amministratore: un file manifest è troppo pesante. Penso che la modifica dei gruppi predefiniti nella nostra applicazione sia probabilmente la soluzione più semplice. – adrianbanks

risposta

9

Il problema è che la protezione di Windows (nota anche come "UAC") sta interferendo. C'è una gestione speciale dei ruoli di amministratore e il tuo utente non avrà effettivamente questi ruoli finché non sarà elevato. I ruoli di amministrazione sono "ghosted" in un certo senso: presenti ma non disponibili per i controlli di autorizzazione o anche per (facilmente) verificare la presenza. Leggere la nota: http://msdn.microsoft.com/en-us/library/46ks97y7.aspx

Ecco una serie che parla del problema, con l'esempio di codice che fa le soluzioni necessarie:

ho risolto un simile problema in un'applicazione ASP.NET creando il mio prompt UAC e utilizzando la password & per chiamare l'API di accesso Win32. Potresti avere la fortuna di trovarti in una app desktop .NET, nel qual caso puoi utilizzare richieste di elevazione regolari.

Ecco un codice C# per controllare le autorizzazioni di amministratore senza elevare.

public const UInt32 TOKEN_DUPLICATE = 0x0002; 
    public const UInt32 TOKEN_IMPERSONATE = 0x0004; 
    public const UInt32 TOKEN_QUERY = 0x0008; 

    public enum TOKEN_ELEVATION_TYPE 
    { 
     TokenElevationTypeDefault = 1, 
     TokenElevationTypeFull, 
     TokenElevationTypeLimited 
    } 

    public enum TOKEN_INFORMATION_CLASS 
    { 
     TokenUser = 1, 
     TokenGroups, 
     TokenPrivileges, 
     TokenOwner, 
     TokenPrimaryGroup, 
     TokenDefaultDacl, 
     TokenSource, 
     TokenType, 
     TokenImpersonationLevel, 
     TokenStatistics, 
     TokenRestrictedSids, 
     TokenSessionId, 
     TokenGroupsAndPrivileges, 
     TokenSessionReference, 
     TokenSandBoxInert, 
     TokenAuditPolicy, 
     TokenOrigin, 
     TokenElevationType, 
     TokenLinkedToken, 
     TokenElevation, 
     TokenHasRestrictions, 
     TokenAccessInformation, 
     TokenVirtualizationAllowed, 
     TokenVirtualizationEnabled, 
     TokenIntegrityLevel, 
     TokenUIAccess, 
     TokenMandatoryPolicy, 
     TokenLogonSid, 
     MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum 
    } 

    public enum SECURITY_IMPERSONATION_LEVEL 
    { 
     SecurityAnonymous, 
     SecurityIdentification, 
     SecurityImpersonation, 
     SecurityDelegation 
    } 


    public static bool IsAdmin() 
    { 
     var identity = WindowsIdentity.GetCurrent(); 
     return (null != identity && new WindowsPrincipal(identity).IsInRole(WindowsBuiltInRole.Administrator)); 
    } 

    /// <summary> 
    /// The function checks whether the primary access token of the process belongs 
    /// to user account that is a member of the local Administrators group, even if 
    /// it currently is not elevated. 
    /// </summary> 
    /// <returns> 
    /// Returns true if the primary access token of the process belongs to user 
    /// account that is a member of the local Administrators group. Returns false 
    /// if the token does not. 
    /// </returns> 
    public static bool CanBeAdmin() 
    { 
     bool fInAdminGroup = false; 
     IntPtr hToken = IntPtr.Zero; 
     IntPtr hTokenToCheck = IntPtr.Zero; 
     IntPtr pElevationType = IntPtr.Zero; 
     IntPtr pLinkedToken = IntPtr.Zero; 
     int cbSize = 0; 

     if (IsAdmin()) 
      return true; 

     try 
     { 
      // Check the token for this user 
      hToken = WindowsIdentity.GetCurrent().Token; 

      // Determine whether system is running Windows Vista or later operating 
      // systems (major version >= 6) because they support linked tokens, but 
      // previous versions (major version < 6) do not. 
      if (Environment.OSVersion.Version.Major >= 6) 
      { 
       // Running Windows Vista or later (major version >= 6). 
       // Determine token type: limited, elevated, or default. 

       // Allocate a buffer for the elevation type information. 
       cbSize = sizeof(TOKEN_ELEVATION_TYPE); 
       pElevationType = Marshal.AllocHGlobal(cbSize); 
       if (pElevationType == IntPtr.Zero) 
       { 
        throw new Win32Exception(Marshal.GetLastWin32Error()); 
       } 

       // Retrieve token elevation type information. 
       if (!GetTokenInformation(hToken, 
        TOKEN_INFORMATION_CLASS.TokenElevationType, pElevationType, cbSize, out cbSize)) 
       { 
        throw new Win32Exception(Marshal.GetLastWin32Error()); 
       } 

       // Marshal the TOKEN_ELEVATION_TYPE enum from native to .NET. 
       TOKEN_ELEVATION_TYPE elevType = (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(pElevationType); 

       // If limited, get the linked elevated token for further check. 
       if (elevType == TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited) 
       { 
        // Allocate a buffer for the linked token. 
        cbSize = IntPtr.Size; 
        pLinkedToken = Marshal.AllocHGlobal(cbSize); 
        if (pLinkedToken == IntPtr.Zero) 
        { 
         throw new Win32Exception(Marshal.GetLastWin32Error()); 
        } 

        // Get the linked token. 
        if (!GetTokenInformation(hToken, 
         TOKEN_INFORMATION_CLASS.TokenLinkedToken, pLinkedToken, 
         cbSize, out cbSize)) 
        { 
         throw new Win32Exception(Marshal.GetLastWin32Error()); 
        } 

        // Marshal the linked token value from native to .NET. 
        hTokenToCheck = Marshal.ReadIntPtr(pLinkedToken); 
       } 
      } 

      // CheckTokenMembership requires an impersonation token. If we just got 
      // a linked token, it already is an impersonation token. If we did not 
      // get a linked token, duplicate the original into an impersonation 
      // token for CheckTokenMembership. 
      if (hTokenToCheck == IntPtr.Zero) 
      { 
       if (!DuplicateToken(hToken, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, ref hTokenToCheck)) 
       { 
        throw new Win32Exception(Marshal.GetLastWin32Error()); 
       } 
      } 

      // Check if the token to be checked contains admin SID. 
      WindowsIdentity id = new WindowsIdentity(hTokenToCheck); 
      WindowsPrincipal principal = new WindowsPrincipal(id); 
      fInAdminGroup = principal.IsInRole(WindowsBuiltInRole.Administrator); 
     } 
     catch 
     { 
      return false; 
     } 
     finally 
     { 
      // Centralized cleanup for all allocated resources. 
      if (pElevationType != IntPtr.Zero) 
      { 
       Marshal.FreeHGlobal(pElevationType); 
       pElevationType = IntPtr.Zero; 
      } 
      if (pLinkedToken != IntPtr.Zero) 
      { 
       Marshal.FreeHGlobal(pLinkedToken); 
       pLinkedToken = IntPtr.Zero; 
      } 
     } 

     return fInAdminGroup; 
    } 

È adattato da un articolo che ho trovato online da qualche parte, mi dispiace, ho perso l'attribuzione.

+0

Una scelta migliore potrebbe essere quella di ripensare la progettazione in modo che non abbia bisogno che l'utente sia un amministratore. Forse si utilizza un servizio Windows di qualche tipo ha diritti di amministratore. Il problema è che, con un'app ASP, stai esponendo i diritti di amministratore all'utente Web, il che potrebbe essere pericoloso se nel tuo codice esiste un difetto che consente alle persone di eseguire il codice arbibario. –

+0

Non c'era modo di evitarlo. Ma hai ragione, è una buona idea avvolgere solo il codice che richiede l'accesso da amministratore in un "eleva/annulla", quindi non c'è esposizione. (che è quello che ho fatto) –

+0

Grande grazie, Steve! –

1

L'applicazione non è elevata. In circostanze normali, l'UAC rimuove l'"amministratore" dell'utente. Se l'app può essere utilizzata solo dagli amministratori, aggiungi un manifest che lo elevi per poter mantenere la loro amministrazione. Se può essere utilizzato da entrambi, la soluzione migliore è dividere in due parti, una con un manifesto di elevazione e una senza, e lanciare la parte elevata da un pulsante o da una voce di menu decorata con lo scudo in modo che gli utenti non facciano clic se non sono amministratori. (Nei sistemi operativi meno recenti il ​​messaggio per mettere lo scudo sul pulsante verrà ignorato.) La ricerca su "UAC", "partition" e "shellexecute" sarà utile.

7

Questo ha funzionato per me - tutto quello che serviva era di verificare se il programma era stato avviato in un ruolo di amministratore:

public static bool IsAdminRole() 
    { 
     AppDomain domain = Thread.GetDomain(); 

     domain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); 
     WindowsPrincipal principle = (WindowsPrincipal)Thread.CurrentPrincipal; 
     return principle.IsInRole(WindowsBuiltInRole.Administrator); 
    } 

spero che qualcuno ritiene che di utilizzo!

Mike

+0

Ottima risposta, l'ho usata! –

3

ho trovato un altro articolo qui su StackOverflow che affronta in un altro modo. L'ho adattato in un metodo qui sotto. L'utilizzo di Windows 7 restituisce true per gli amministratori, false per i non amministratori e true per i non amministratori quando "Esegui come amministratore". Sembra che funzionerà solo con .Net 3.5 e XP SP2 e versioni successive, sulla base di una prima occhiata a MSDN per la classe PrincipleContext.

private static bool IsUserAdmin() 
{ 
    bool isAdmin = false; 

    WindowsIdentity wi = WindowsIdentity.GetCurrent(); 
    WindowsPrincipal wp = new WindowsPrincipal(wi); 
    isAdmin = wp.IsInRole(WindowsBuiltInRole.Administrator); 

    Console.WriteLine(isAdmin); // False for Windows 7 even if user is admin 

    //found the code below at [http://stackoverflow.com/questions/1089046/in-net-c-test-if-user-is-an-administrative-user][1] 

    // Add reference to System.DirectoryServices.AccountManagement (Add Referemce -> .Net) 
    // Add using System.DirectoryServices.AccountManagement; 

    if (!isAdmin) //PrincipleContext takes a couple seconds, so I don't use it if not necessary 
    { 
     using (PrincipalContext pc = new PrincipalContext(ContextType.Machine, null)) 
     { 
      UserPrincipal up = UserPrincipal.Current; 
      GroupPrincipal gp = GroupPrincipal.FindByIdentity(pc, "Administrators"); 
      if (up.IsMemberOf(gp)) 
      { 
       isAdmin = true; 
      } 
     } 
    } 
    Console.WriteLine(isAdmin); // True for Windows 7 if user is admin 


    return isAdmin; 
}