How To: ¿Como saber si el usuario actual es administrador del dominio?

Nota: Es una pregunta que me encuentro de forma recurrente en los foros de desarrollo, así que lo apunto aquí para tener una referencia.


El escenario


Cuando desarrollamos una aplicación de escritorio, puede ser interesante saber a qué grupos pertenece el usuario que está ejecutando nuestra aplicación, para mostrar / ocultar / permitir / revocar ciertas acciones, u opciones. Por ejemplo, yo acostumbro a tener un botón en la barra de estado de mis aplicaciones que permite cambiar la cadena de conexión, y evidentemente, solo está visible cuando el usuario pertenece al grupo “Administradores del dominio”.


privileges


IsInRole


Para ello, el objeto WindowsPrincipal dispone de un método IsInRole, que nos dirà si un usuario pertenece a un grupo determinado. Genial, además este método tiene varias sobrecargas, de modo que podemos usarlo pasando el RID, SID, el nombre e incluso una constante basada en la enumeración WindowsBuiltIOnRole:


Nombre de miembro Descripción
AccountOperator Los operadores de cuentas administran las cuentas de los usuarios de un equipo o dominio.
Administrator Los administradores tienen acceso completo y sin restricciones al equipo o dominio.
BackupOperator Los operadores de copia de seguridad pueden reemplazar las restricciones de seguridad con el único propósito de hacer copias de seguridad de los archivos o de restaurarlas.
Guest Los invitados tienen más restricciones que los usuarios.
PowerUser Los usuarios avanzados poseen la mayoría de los permisos administrativos, con algunas restricciones. De este modo, los usuarios avanzados pueden ejecutar aplicaciones heredadas, además de aplicaciones certificadas.
PrintOperator Los operadores de impresión pueden tomar el control de una impresora.
Replicator Los replicadores permiten la duplicación de archivos en un dominio.
SystemOperator Los operadores del sistema administran un equipo en particular.
User Los usuarios no pueden realizar cambios accidentales o intencionados en todo el sistema. En consecuencia, pueden ejecutar aplicaciones certificadas, pero no la mayoría de las aplicaciones heredadas. 

De modo que para saber si nuestro usuario es administrador local, basta con hacer esto:


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

Sencillo, verdad? A partir del usuario que ejecuta nuestra aplicación (válido también en caso de impersonación), creamos un objeto Principal e invocamos al método pasándole el grupo contra el que deseamos validar.


El problema de esta enumeración es que como su nombre indica, sólo contempla los grupos locales. Así que si queremos saber si pertenece a un grupo del dominio parece que tendremos que buscar el SID del grupo, o el nombre, y hardcodearlo ‘a mano’ en nuestra aplicación.


Feo verdad? Pues la verdad es que si, muy feo… vamos a investigar un poco más, a ver si encontramos otra forma.


Nota: Por motivos de rendimiento, para determinar la función del usuario se recomienda utilizar la sobrecarga de IsInRole(SecurityIdentifier) como sobrecarga preferible.


WellKnownSidType


Existe una enumeración llamada WellKnownSidType, que devuelve los identificadores de seguridad más utilizados, vamos a darle un vistazo:


Member name

Description
NullSid Indicates a null SID.
WorldSid Indicates a SID that matches everyone.
LocalSid Indicates a local SID.
CreatorOwnerSid Indicates a SID that matches the owner or creator of an object.
CreatorGroupSid Indicates a SID that matches the creator group of an object.
CreatorOwnerServerSid Indicates a creator owner server SID.
CreatorGroupServerSid Indicates a creator group server SID.
NTAuthoritySid Indicates a SID for the Windows NT authority.
DialupSid Indicates a SID for a dial-up account.
NetworkSid Indicates a SID for a network account. This SID is added to the process of a token when it logs on across a network.
BatchSid Indicates a SID for a batch process. This SID is added to the process of a token when it logs on as a batch job.
InteractiveSid Indicates a SID for an interactive account. This SID is added to the process of a token when it logs on interactively.
ServiceSid Indicates a SID for a service. This SID is added to the process of a token when it logs on as a service.
AnonymousSid Indicates a SID for the anonymous account.
ProxySid Indicates a proxy SID.
EnterpriseControllersSid Indicates a SID for an enterprise controller.
SelfSid Indicates a SID for self.
AuthenticatedUserSid Indicates a SID for an authenticated user.
RestrictedCodeSid Indicates a SID for restricted code.
TerminalServerSid Indicates a SID that matches a terminal server account.
RemoteLogonIdSid Indicates a SID that matches remote logons.
LogonIdsSid Indicates a SID that matches logon IDs.
LocalSystemSid Indicates a SID that matches the local system.
LocalServiceSid Indicates a SID that matches a local service.
NetworkServiceSid Indicates a SID that matches a network service.
BuiltinDomainSid Indicates a SID that matches the domain account.
BuiltinAdministratorsSid Indicates a SID that matches the administrator account.
BuiltinUsersSid Indicates a SID that matches built-in user accounts.
BuiltinGuestsSid Indicates a SID that matches the guest account.
BuiltinPowerUsersSid Indicates a SID that matches the power users group.
BuiltinAccountOperatorsSid Indicates a SID that matches the account operators account.
BuiltinSystemOperatorsSid Indicates a SID that matches the system operators group.
BuiltinPrintOperatorsSid Indicates a SID that matches the print operators group.
BuiltinBackupOperatorsSid Indicates a SID that matches the backup operators group.
BuiltinReplicatorSid Indicates a SID that matches the replicator account.
BuiltinPreWindows2000CompatibleAccessSid Indicates a SID that matches pre-Windows 2000 compatible accounts.
BuiltinRemoteDesktopUsersSid Indicates a SID that matches remote desktop users.
BuiltinNetworkConfigurationOperatorsSid Indicates a SID that matches the network operators group.
AccountAdministratorSid Indicates a SID that matches the account administrators group.
AccountGuestSid Indicates a SID that matches the account guest group.
AccountKrbtgtSid Indicates a SID that matches the account Kerberos target group.
AccountDomainAdminsSid Indicates a SID that matches the account domain administrator group.
AccountDomainUsersSid Indicates a SID that matches the account domain users group.
AccountDomainGuestsSid Indicates a SID that matches the account domain guests group.
AccountComputersSid Indicates a SID that matches the account computer group.
AccountControllersSid Indicates a SID that matches the account controller group.
AccountCertAdminsSid Indicates a SID that matches the certificate administrators group.
AccountSchemaAdminsSid Indicates a SID that matches the schema administrators group.
AccountEnterpriseAdminsSid Indicates a SID that matches the enterprise administrators group.
AccountPolicyAdminsSid Indicates a SID that matches the policy administrators group.
AccountRasAndIasServersSid Indicates a SID that matches the RAS and IAS server account.
NtlmAuthenticationSid Indicates a SID present when the Microsoft NTLM authentication package authenticated the client.
DigestAuthenticationSid Indicates a SID present when the Microsoft Digest authentication package authenticated the client.
SChannelAuthenticationSid Indicates a SID present when the Secure Channel (SSL/TLS) authentication package authenticated the client.
ThisOrganizationSid Indicates a SID present when the user authenticated from within the forest or across a trust that does not have the selective authentication option enabled. If this SID is present, then OtherOrganizationSid cannot be present.
OtherOrganizationSid Indicates a SID present when the user authenticated across a forest with the selective authentication option enabled. If this SID is present, then ThisOrganizationSid cannot be present.
BuiltinIncomingForestTrustBuildersSid Indicates a SID that allows a user to create incoming forest trusts. It is added to the token of users who are a member of the Incoming Forest Trust Builders built-in group in the root domain of the forest.
BuiltinPerformanceMonitoringUsersSid Indicates a SID that matches the group of users that have remote access to schedule logging of performance counters on this computer.
BuiltinPerformanceLoggingUsersSid Indicates a SID that matches the group of users that have remote access to monitor the computer.
BuiltinAuthorizationAccessSid Indicates a SID that matches the Windows Authorization Access group.
WinBuiltinTerminalServerLicenseServersSid Indicates a SID is present in a server that can issue Terminal Server licenses.
MaxDefined Indicates the maximum defined SID in the WellKnownSidType enumeration.

BINGO!!! Parece que tenemos el SID del grupo de admnistradores del dominio (lo he marcado en rojo en la tabla anterior).


Ahora vamos a generar el SID del grupo de adminstradores del dominio y ya podemos volver a probar el método IsInRole:


WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.AccountDomainAdminsSid, null);
return wp.IsInRole(sid);

Ops! Nuestro gozo en un pozo… se necesita informar el segundo argumento del constructor para el SID del grupo:


DomainSidError


DomainSid


¿Y que kkgrnn$# representa que es este identificador? Pues según pone en la ayuda del constructor, debe proporcionarse el SID del dominio para que el constructor pueda devolver algunos identificadores de WellKnownSidType, entre los cuales está el de los administradores del dominio.


Dicho de otro modo, o sabemos el SID de nuestro dominio o todo lo anterior no vale para nada… :-(


¿Y cómo podemos saber el SID de dominio? Después de buscar un ratito, lo único que he encontrado es una utilidad de consola llamada PsGetSid, que forma parte de las PSTools del inefable Mark Russinovich. Basta descargar esta utilidad y ejecutarla desde la consola de este modo para saber el SID de nuestro dominio (el nombre de dominio en formato “microsoft.com” o “net.volvo.com”):


DomainSidConsole


Sin embargo, me niego a tener que hacer esto para saber el identificador del dominio. Así que vamos a probar si podemos recuperar esta propiedad del esquema de AD mediante un DirectoryEntry. Para ello utilizaremos la clase Domain:


Domain d = Domain.GetDomain(new
    DirectoryContext(DirectoryContextType.Domain, getDomainName()));
using (DirectoryEntry de = d.GetDirectoryEntry())
{
    byte[] domSid = (byte[])de.Properties["objectSid"].Value;
    string sdomainSid = sIDtoString(domSid);
    Console.WriteLine(sdomainSid);
}   

Nota: Aquí necesitaremos dos funciones de apoyo, la primera nos devuelve el nombre del domino, y la segunda transforma el array de bits del SID en su representación textual:


public static string getDomainName()
{
    return IPGlobalProperties.GetIPGlobalProperties().DomainName;
}

public static string sIDtoString(byte[] sidBinary)
{
    SecurityIdentifier sid = new SecurityIdentifier(sidBinary, 0);
    return sid.ToString();
}

A todo esto el valor de la variable sdomainSid es el esperado!!! :-D


 


 


 


 


 


 


 


Poniéndolo todo junto


Al igual que el alegre bandolero, también soy un fanático de los métodos extensores, así que vamos a encapsular todo esto en un método que extienda la clase WindowsIdentity. Aquí va todo el código junto:


 
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.Net.NetworkInformation;
using System.Security.Principal;
 
namespace Alpha.Code
{
    public static class SecurityExtensions
    {
        public static bool IsDomainAdmin (this WindowsIdentity identity)
        {
            Domain d = Domain.GetDomain(new
                DirectoryContext(DirectoryContextType.Domain, getDomainName()));
            using (DirectoryEntry de = d.GetDirectoryEntry())
            {
                byte[] bdomSid = (byte[])de.Properties["objectSid"].Value;
                string sdomainSid = sIDtoString(bdomSid);
                WindowsPrincipal wp = new WindowsPrincipal(identity);
                SecurityIdentifier dsid = new SecurityIdentifier(sdomainSid);
                SecurityIdentifier dasid = new SecurityIdentifier(
                    WellKnownSidType.AccountDomainAdminsSid, dsid);
                return wp.IsInRole(dasid);
            }
        }
 
        public static string getDomainName()
        {
            return IPGlobalProperties.GetIPGlobalProperties().DomainName;
        }
 
        public static string sIDtoString(byte[] sidBinary)
        {
            SecurityIdentifier sid = new SecurityIdentifier(sidBinary, 0);
            return sid.ToString();
        }
    }
}

Y la forma de usarlo es tan sencilla como esto:


if (WindowsIdentity.GetCurrent().IsDomainAdmin())
{
    //Acciones a realizar si el usuario es administrador de dominio... 
}

Un saludo desde las frías tierras de Andorra :-)


Noviembre 2009


** crossposting desde el blog de LluĂ­s Franco en geeks.ms **

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>