2010-04-20 11 views
8

Usamos autenticación NTLM en nuestra aplicación para determinar si un usuario puede realizar ciertas operaciones. Usamos el IPrincipal de su inicio de sesión de Windows actual (en aplicaciones WinForms), llamando al IsInRole para verificar membresías de grupos específicos.Llamando a IPrincipal.IsInRole en Windows 7

Para comprobar que el usuario es un administrador local en la máquina, utilizamos:

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

Esto funciona si el usuario actual es el usuario Administrator, o es otro usuario que sea miembro del grupo Builtin\Administrators .

En nuestras pruebas en Windows 7, hemos encontrado que esto ya no funciona como se esperaba. El usuario Administrator sigue funcionando bien, pero cualquier otro usuario que sea miembro del grupo Builtin\Administrators devuelve falso para la llamada IsInRole.

¿Qué podría estar causando esta diferencia? Tengo la corazonada de que una configuración predeterminada ha cambiado en algún lugar (posible en gpedit), pero no puede encontrar nada que se parece al culpable.

+0

Después de hacer esta pregunta, tuve un juego con algún código que verificaba manualmente las membresías del grupo y replicaba el comportamiento en una máquina Vista. Eso me llevó a la idea de que UAC era el problema: la mayoría de las máquinas que ejecutan Vista aquí tienen el UAC desactivado. La aplicación comprueba la pertenencia a grupos definidos a partir de otra fuente, por lo que puede variar si la aplicación debe ejecutarse como administrador: un archivo de manifiesto es demasiado brusco. Creo que cambiar los grupos predeterminados en nuestra aplicación es probablemente la solución más fácil. – adrianbanks

Respuesta

9

El problema es que la seguridad de Windows (también conocida como "UAC") se interpone en su camino. Hay un manejo especial de los roles de administrador y su usuario no tendrá estos roles hasta que se eleve. Los roles de administrador están "fantasma" en un sentido: presente pero no disponible para verificaciones de permisos o incluso para (fácilmente) evaluar la presencia. Véase la nota a: http://msdn.microsoft.com/en-us/library/46ks97y7.aspx

Aquí es una serie que habla sobre el tema, con el ejemplo de código que hace las soluciones necesarias:

me resolvió un similares problema en una aplicación ASP.NET construyendo mi propio prompt UAC y usando la contraseña de nombre & para llamar a la API de inicio de sesión de Win32. Es posible que tenga la suerte de estar en una aplicación de escritorio .NET, en cuyo caso puede usar solicitudes de elevación regulares.

Aquí hay algunos códigos C# para comprobar los permisos de administrador sin elevarlos.

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; 
    } 

Es una adaptación de un artículo que encontré en línea en algún lado, perdón, perdí la atribución.

+0

Una mejor opción podría ser reconsiderar su diseño para que no sea necesario que el usuario sea un administrador. Tal vez use un servicio de Windows de algún tipo tiene derechos de administrador. El problema es que, con una aplicación ASP, estás exponiendo los derechos de administrador al usuario web, lo que podría ser malo si existe una falla en tu código que permita a las personas ejecutar código arbibary. –

+0

No había forma de evitarlo. Pero tienes razón, es una buena idea ajustar solo el código que necesita acceso de administrador en un "elevar/deshacer" para que no haya exposición. (que es lo que hice) –

+0

¡Muchas gracias, Steve! –

1

Su aplicación no es elevada. En circunstancias normales, UAC elimina la "capacidad de administrador" del usuario. Si la aplicación solo puede ser utilizada por los administradores, agregue un manifiesto que haga que se eleve para que puedan mantener su administración. Si puede ser utilizado por cualquiera de los dos, la mejor opción es dividirlo en dos partes, una con un manifiesto de elevación y otra sin él, y ejecutar la parte elevada desde un botón o elemento de menú que está decorado con el escudo para que los usuarios no hagan clic si no son administradores. (En sistemas operativos anteriores, se ignorará el mensaje para colocar el escudo en el botón). Buscar en "UAC", "partición" y "poner en cortocircuito" será útil.

7

Esto funcionó para mí - todo lo que necesitaba era comprobar si el programa se había iniciado en un rol de administrador:

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

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

Esperanza alguien encuentra la de uso!

Mike

+0

¡Gran respuesta, lo usé! –

3

he encontrado otro artículo aquí en stackoverflow que aborda esto de otra manera. Lo adapté a un método a continuación. Al usar Windows 7, esto devolvió verdadero para administradores, falso para no administradores y verdadero para no administrador cuando 'Ejecutar como administrador'. Parece que esto solo funcionará con .Net 3.5 y XP SP2 y posterior, en base a una primera ojeada a MSDN para la clase 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; 
}