RSS 2.0
# Friday, July 07, 2006

Updated August 13, 2006 to reflect Sander's comment.

On my old blog (in Dutch) I commented that I thought the WindowsImpersonationContext was clumsy and I wrote a replacement that you can use as follows:

Console.WriteLine("Current user: " + WindowsIdentity.GetCurrent().Name);
WrapperImpersonationContext context = new WrapperImpersonationContext(domain, username, password);
context.Enter();
// Execute code under other uses context
Console.WriteLine("Current user: " + WindowsIdentity.GetCurrent().Name);
context.Leave();
Console.WriteLine("Current user: " + WindowsIdentity.GetCurrent().Name);

Recently a visitor noted that the code wasn't quite right (missng a few using statements, and I found some other small issues. The correct code (including namespace references) is below. It takes care of all that nasty calls into the Win32 API. The one thing you need to be aware of is that it requires permissions to call into a DLL (i.e. run unsafe code), which is why I added the attributes that indicate this. Unfortunately that renders this class useless in a hosted environment, unless you strong sign the assembly and pursuade the host to allow your assembly to run in Full or High trust. This however is a problem you will run into regardless your use of this class. As soon as you call LogonUser you need at least High trust. If this is something that should be possible under lower trust by default it's up to the folks in Redmond to add this functionality to .NET and handle it as such.

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
using System.ComponentModel;

public class WrapperImpersonationContext
{
   [DllImport("advapi32.dll", SetLastError = true)]
   public static extern bool LogonUser(String lpszUsername, String lpszDomain,
   String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

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

   private const int LOGON32_PROVIDER_DEFAULT = 0;
   private const int LOGON32_LOGON_INTERACTIVE = 2;

   private string m_Domain;
   private string m_Password;
   private string m_Username;
   private IntPtr m_Token;

   private WindowsImpersonationContext m_Context = null;

   protected bool IsInContext
   {
      get { return m_Context != null; }
   }

   public WrapperImpersonationContext(string domain, string username, string password)
   {
      m_Domain = domain;
      m_Username = username;
      m_Password = password;
   }

   [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
   public void Enter()
   {
      if (this.IsInContext) return;
      m_Token = new IntPtr(0);
      try
      {
         m_Token = IntPtr.Zero;
         bool logonSuccessfull = LogonUser(
            m_Username,
            m_Domain,
            m_Password,
            LOGON32_LOGON_INTERACTIVE,
            LOGON32_PROVIDER_DEFAULT,
            ref m_Token);
         if (logonSuccessfull == false)
         {
            int error = Marshal.GetLastWin32Error();
            throw new Win32Exception(error);
         }
         WindowsIdentity identity = new WindowsIdentity(m_Token);
         m_Context = identity.Impersonate();
      }
      catch (Exception exception)
      {
         // Catch exceptions here
      }
   }

   [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
   public void Leave()
   {
       if (this.IsInContext == false) return;
       m_Context.Undo();

       if (m_Token != IntPtr.Zero) CloseHandle(m_Token);
       m_Context = null;
   }
}

 

Friday, July 07, 2006 9:18:44 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [3] -
.NET | Development | English
Sunday, August 06, 2006 9:07:29 PM (W. Europe Daylight Time, UTC+02:00)
according to the compiler you need to compile with /unsafe for this class to work. However, this applies only to the method FormatMessage, since it uses the C++ type pointer IntPtr* Arguments.
But the class doesn't use this method anyway. So by removing FormatMessage, you don't need permissions to run unsafe code.
Sunday, August 13, 2006 9:16:08 PM (W. Europe Daylight Time, UTC+02:00)
Sander, you are absolutely right. That's what you get when copying from a class that does more... sorry. That said, you will still need High or Full trust, because otherwise the call to LogonUser is still not allowed. Not having to add the /usafe compile switch is a plus, but it doesn't matter once you deploy this.
Tuesday, July 22, 2008 4:05:01 PM (W. Europe Daylight Time, UTC+02:00)
Hi Michiel,

maybe late than never but thanks for this source :)

Thanks!
All comments require the approval of the site owner before being displayed.
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, strike) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview
Sign In

Archive
<February 2012>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910
About
This is the blog of Michiel van Otegem, a Senior Software Architect with Sogeti Netherlands, and author of several books and numerous articles on (ASP).NET, XML, and related technologies.
Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2012
Michiel van Otegem
All Content © 2012, Michiel van Otegem
DasBlog theme 'Business' created by Christoph De Baene (delarou)