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. |