Como Determinar se o Usuário é Membro do Grupo Administrador no Windows 7 com o UAC habilitado

Com o UAC habilitado no nível padrão, todas as contas de usuário, incluindo contas administrativas, executam com direitos de usuário padrão.

Quando um usuário faz logon no Windows, o sistema cria um token de acesso para ele. Esse token contém informações sobre o nível de acesso que é concedido ao usuário, incluindo os identificadores de segurança (SIDs) e privilégios do Windows. Quando um administrador faz logon, o Windows cria dois tokens de acesso separados para o usuário: um token de acesso sem privilégios administrativos (token filtrado) e um token de acesso de administrador (token full).

Para maiores detalhes sobre UAC veja o artigo: Controle de Conta de Usuário (UAC).

O UAC é um grande avanço para a segurança. No entanto, esse avanço quebra alguns paradigmas que você pode estar acostumado a usar para desenvolver aplicativos. Por exemplo, verificar se o usuário é administrador.

Se você utiliza a API IsUserAnAdmin do shell32 ou as classes WindowsIdentity e WindowsPrincipal do .NET Framework (veja o fragmento de código a seguir), você descobrirá que eles retornam verdadeiro quando o processo estiver elevado e falso caso contrário. 

 bool IsUserAdministrator()
 {
 WindowsIdentity identity = WindowsIdentity.GetCurrent();
 WindowsPrincipal principal = new WindowsPrincipal(identity);
 return principal.IsInRole (WindowsBuiltInRole.Administrator);
 }
 

O resultado está correto, entretanto, em alguns casos o valor não informa se o usuário é administrador independentemente se a aplicação está sendo executada elevada.

A API GetTokenInformation do advapi32.dll permite determinar qual é o token que o usuário está utilizando e com isso é possível assumir (isso mesmo, veja mais adiante) se o usuário é administrador independemente se o processo está ou não elevado.  

 

 [DllImport("advapi32.dll", SetLastError = true)]
 static extern bool GetTokenInformation(IntPtr tokenHandle, TokenInformationClass tokenInformationClass, IntPtr tokenInformation, int tokenInformationLength, out int returnLength);
 
 /// <summary>
 /// Passed to <see cref="GetTokenInformation"/> to specify what
 /// information about the token to return.
 /// </summary>
 enum TokenInformationClass
 {
 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
 }
 
 /// <summary>
 /// The elevation type for a user token.
 /// </summary>
 enum TokenElevationType
 {
 TokenElevationTypeDefault = 1,
 TokenElevationTypeFull,
 TokenElevationTypeLimited
 }
 
 
 bool IsUserAnAdministrator() {
 
 WindowsIdentity identity = WindowsIdentity.GetCurrent();
 WindowsPrincipal principal = new WindowsPrincipal(identity);
 
 // Verifica se o usuário tem a role de Admin. Caso tiver retorna verdadeiro.
 // Se o UAC estiver habilitado e o processo não estiver elevado a verificação abaixo retornará falso 
 //independentemente se o usuário pertencer ao grupo administrador
 if (principal.IsInRole(WindowsBuiltInRole.Administrator)) return true;
 
 // Se o processo não estiver executando a partir do Vista, não é necessário realizar a verificação do UAC.
 if (Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version.Major < 6)
 {
 return false;
 }
 
 int tokenInfLength = Marshal.SizeOf(typeof(int));
 IntPtr tokenInformation = Marshal.AllocHGlobal(tokenInfLength);
 
 try
 {
 var token = identity.Token;
 var result = GetTokenInformation(token, TokenInformationClass.TokenElevationType, tokenInformation, tokenInfLength, out tokenInfLength);
 
 if (!result)
 {
 var exception = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
 throw new InvalidOperationException("Não foi possível obter a informação do TOKEN", exception);
 }
 
 var elevationType = (TokenElevationType)Marshal.ReadInt32(tokenInformation);
 
 switch (elevationType)
 {
 case TokenElevationType.TokenElevationTypeDefault:
 // TokenElevationTypeDefault - O usuário possui apenas um token, portanto não é administrador.
 return false;
 case TokenElevationType.TokenElevationTypeFull:
 // TokenElevationTypeFull - O usuário possui dois tokens e o processo está sendo executado elevado.
 return true;
 case TokenElevationType.TokenElevationTypeLimited:
 // TokenElevationTypeLimited - O usuário possui dois tokens mas o processo não está sendo executado elevado. 
 return true;
 default:
 // Não foi possível determinar qual o token do usuário.
 return false;
 }
 }
 finally
 {
 if (tokenInformation != IntPtr.Zero) Marshal.FreeHGlobal(tokenInformation);
 }
 
 }
 

 

Observe que essa técnica apenas detecta qual é o token que o usuário possui. Na maioria das situações, isso irá determinar se o usuário está executando como administrador. No entanto, existem outros tipos de usuários com permissões avançadas que podem gerar uma divisão de token durante um logon interativo (por exemplo, o grupo operadores de configuração de rede). Se você estiver usando um desses grupos de permissão avançada, esta técnica irá determinar o tipo de elevação e não a presença (ou ausência) de credenciais de administrador.

Se o UAC estiver desabilitado, a API irá retornar apenas o TOKEN TokenElevationTypeDefault. Caso o usuário estiver logado como administrador o nosso código de exemplo irá identificá-lo erroneamente como usuário comum. Por isso, nesse caso, é importante combinar os dois tipos de verificação (IsUserAnAdmin ou WindowsIdentity e WindowsPrincipal) com o TOKEN.

Para finalizar, a não ser que você realmente precise e saiba o que está fazendo, não utilize esses mecanismos de verificação. Caso você precise de direitos administrativos, crie, por exemplo, um manifesto para sua aplicação e defina que a aplicação precisa ser executada com privilégios administrativos.

No Visual Studio é possível definir essa configuração através do manifesto da aplicação localizado na pasta properties do projeto, conforme:

 

  <?xml version="1.0" encoding="utf-8"?>
 <asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance">
 <assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
 <security>
 <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
 <!-- UAC Manifest Options
 If you want to change the Windows User Account Control level replace the 
 requestedExecutionLevel node with one of the following.
 
 <requestedExecutionLevel level="asInvoker" uiAccess="false" />
 <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
 <requestedExecutionLevel level="highestAvailable" uiAccess="false" />
 
 If you want to utilize File and Registry Virtualization for backward 
 compatibility then delete the requestedExecutionLevel node.
 -->
 
 <requestedExecutionLevel level="asInvoker" uiAccess="false" />
 </requestedPrivileges>
 <applicationRequestMinimum>
 <defaultAssemblyRequest permissionSetReference="Custom" />
 <PermissionSet class="System.Security.PermissionSet" version="1" ID="Custom" SameSite="site" Unrestricted="true" />
 </applicationRequestMinimum>
 </security>
 </trustInfo>
 </asmv1:assembly>
 
  

A tabela a seguir descreve os tipos de execução:

 

Nível de Execução

Descrição

asInvoker

Executa a aplicação com o token atual. Nunca solicita elevação.

highestAvailable

Tenta usar os direitos de usuário mais alto disponíveis. Solicitará o modo de aprovação  de administrador caso o usuário for administrador.

requireAdministrator

Solicitará elevação, a menos que ser que o processo pai já esteja elevado.