4 votes

Double impersonnation dans un service WCF ?

Mon installation est un peu compliquée, alors je vais commencer par ça.

Nous avons un service Web WCF qui obtient des données de diverses sources par le biais de plusieurs API différentes et renvoie ces données au client. La sécurité demandée est que cela soit fait sur HTTPS (travail). Le standard IIS est que le pool d'applications doit être configuré pour utiliser le compte de service réseau IIS de base et que l'usurpation d'identité .net doit être utilisée.

Mon problème est que le service web doit toujours s'exécuter sous un identifiant de processus AD, quelle que soit la personne qui l'appelle, mais qu'il doit aussi vérifier dans quels groupes AD se trouve l'appelant pour déterminer quelles fonctions sont accessibles. Maintenant, je peux configurer mon web.config pour qu'il utilise et cela fonctionne en quelque sorte pour qu'il s'exécute toujours en tant que Blah, mais je ne sais pas comment faire pour qu'il usurpe l'identité de l'utilisateur qui appelle et vérifie à quelles fonctions il a accès.

**mise à jour : j'ai oublié de mentionner que le client appelant devrait pouvoir passer un UN/PASS au lieu de son seul jeton Windows. Le service wcf devrait vérifier qu'il s'agit d'un UN et d'un PASS AD valides et demander dans quels groupes il se trouve.

3voto

Randolpho Points 36512

On dirait que vous voulez HostingEnvironment.Impersonate

Exemple :

using (var imp = HostingEnvironment.Impersonate())
{
    // code now executes in the context of the authenticated user, 
    // rather than the service account
}

Cela fonctionne à merveille, malheureusement la norme ici est de ne pas utiliser les pools d'applications car la gestion des mots de passe est plus facile pour eux si c'est à chaque équipe de les maintenir à jour en les mettant dans le web.config.

Eh bien, cela semble contre-intuitif, mais j'ai rencontré des politiques pires que ça dans ma vie, alors je ne suis guère en mesure de juger. ;)

Comme je l'ai mentionné dans mon commentaire, il existe des surcharges de Impersonate qui vous permettront d'usurper l'identité d'un compte arbitraire. Pour ce faire, vous devez obtenir le jeton d'identité Windows pour cet utilisateur, ce qui n'est pas trivial, et pas, à ma connaissance, quelque chose que vous pouvez faire à 100% en code géré. Vous devrez utiliser du code non géré, et vous devrez connaître le nom d'utilisateur et le mot de passe du compte usurpé dans votre application. C'est beaucoup moins sûr que de simplement définir le compte comme l'ID du pool d'applications, si vous voulez discuter de ce point avec votre architecte réseau, BTW. Juste quelques munitions pour vous.

Quoi qu'il en soit, voici un exemple de code que j'ai adapté des internets :

#region native imports. 
public const int Logon_LogonTypeInteractive = 2;
public const int Logon_ProviderDefault = 0;
public const int Duplicate_ImpersonationLevelImpersonate = 2;

[DllImport("advapi32.dll")]
public static extern bool LogonUser(string lpszUserName, string lpszDomain, string lpszPassword,
    int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool RevertToSelf();

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern  bool CloseHandle(IntPtr handle);
#endregion native imports. 

#region elsewhere...
public IntPtr GetWindowsTokenForImpersonation(string username, string password, string domain)
{
    IntPtr loginToken = IntPtr.Zero;
    IntPtr workingToken = IntPtr.Zero;
    bool success
    if(!RevertToSelf()) 
    {
        return IntPtr.Zero;
        // failed to eliminate any existing impersonations. This block may not be necessary depending on your code
    } 
    if(!LogonUserA(username, domain, password, Logon_LogonTypeInteractive, Logon_ProviderDefault, ref loginToken))
    {
        return IntPtr.Zero;
        // failed to log in the user
    }

    if(!DuplicateToken(loginToken, Duplicate_ImpersonationLevelImpersonate, ref workingToken)
    {
        if(loginToken != IntPtr.Zero)
        {
            CloseHandle(loginToken);
        }
        return IntPtr.Zero;
        // failed to get a working impersonation token
    }

    CloseHandle(loginToken);
    return workingToken; // NOTE: You must dispose this token manually using CloseHandle after impersonation is complete. 
}
#endregion elsewhere

#region where you want to impersonate

var token = GetWindowsTokenForImpersonation(username, password, domain);
if(token != IntPtr.Zero)
{
    using(var imp = HostingEnvironment.Impersonate(token))
    {
        // code here executes under impersonation
    }
    CloseHandle(token);
}
#endregion

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X