Implementing impersonation in c#

Hi,
Today am going to explain about the implementation of impersonation. This code we can use in many contexts like web, windows, services etc. The main things to be a pr requisite are user context should have a proper permissions and necessary references were made.( Please see the ‘usings’ here in the snippet)

This impersonation is needed mainly in few cases like to do some server side functionality means printing, network printing accessing, checking available printers, IO operations, etc all comes under particular user context.

This thing needs to be done in many cases as the IIS application runs under very less privileges. So the IIS_User need to be impersonated before doing few operations.
Here am attaching the complete class for making it more simpler to benefit you people. You just need to copy this class in to your project and use a snippet as like this

using (Impersonation impersonation = new Impersonation(impersonationUsername, impersonationDomainName, impersonationPWD))
{

}

And here is the code snippet required.

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


[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public class Impersonation : IDisposable
{

    /// 
    /// Initializes a new instance of the  class.
    /// 
    /// Name of the user.
    /// Name of the domain.
    /// The password.
    public Impersonation(string userName, string domainName, string password)
    {
        ImpersonateValidUser(userName, domainName, password);
    }

    public void Dispose()
    {
        UndoImpersonation();
    }

    #region P/Invoke.
    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern int LogonUser(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);

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

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

    [DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    private static extern int FormatMessage(int dwFlags, ref IntPtr lpSource,
        int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, IntPtr Arguments);

    [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool LoadUserProfile
    (IntPtr hToken, ref ProfileInfo lpProfileInfo);

    [DllImport("Userenv.dll", CallingConvention =
    CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool UnloadUserProfile
    (IntPtr hToken, IntPtr lpProfileInfo);

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

    #endregion

    #region Private member.
    // ------------------------------------------------------------------
    /// 
    /// Impersonates the valid user.
    /// 
    /// Name of the user.
    /// The domain.
    /// The password.
    private void ImpersonateValidUser(string userName, string domain, string password)
    {
        WindowsIdentity tempWindowsIdentity = null;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        try
        {
            if (RevertToSelf())
            {
                if (LogonUser(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
                {
                    if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                    {
                        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                        impersonationContext = tempWindowsIdentity.Impersonate();
                    }
                }
            }
        }
        catch (Exception ex)
        {
            EmdsLogger.AddToLog(ex);

        }
        finally
        {
            if (token != IntPtr.Zero)
            {
                CloseHandle(token);
            }
            if (tokenDuplicate != IntPtr.Zero)
            {
                CloseHandle(tokenDuplicate);
            }
        }
    }

    // GetErrorMessage formats and returns an error message
    // corresponding to the input errorCode.
    public static string GetErrorMessage(int errorCode)
    {
        int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
        int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
        int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

        //int errorCode = 0x5; //ERROR_ACCESS_DENIED
        //throw new System.ComponentModel.Win32Exception(errorCode);

        int messageSize = 255;
        String lpMsgBuf = "";
        int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;

        IntPtr ptrlpSource = IntPtr.Zero;
        IntPtr prtArguments = IntPtr.Zero;

        int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, prtArguments);
        if (0 == retVal)
        {
            //throw new Exception("Failed to format message for error code " + errorCode + ". ");
            //EmdsLogger.AddToLog("Impersonation", "Failed to format message for error code " + errorCode + ". ");
        }

        return lpMsgBuf;
    }

    /// 
    /// Undoes the impersonation.
    /// 
    private void UndoImpersonation()
    {
        if (impersonationContext != null)
        {
            impersonationContext.Undo();
        }
    }

    private WindowsImpersonationContext impersonationContext = null;

    // ------------------------------------------------------------------
    #endregion
}

[StructLayout(LayoutKind.Sequential)]
public struct ProfileInfo
{
    ///
    /// Specifies the size of the structure, in bytes.
    ///
    public int dwSize;

    ///
    /// This member can be one of the following flags: 
    /// PI_NOUI or PI_APPLYPOLICY
    ///
    public int dwFlags;

    ///
    /// Pointer to the name of the user.
    /// This member is used as the base name of the directory 
    /// in which to store a new profile.
    ///
    public string lpUserName;

    ///
    /// Pointer to the roaming user profile path.
    /// If the user does not have a roaming profile, this member can be NULL.
    ///
    public string lpProfilePath;

    ///
    /// Pointer to the default user profile path. This member can be NULL.
    ///
    public string lpDefaultPath;

    ///
    /// Pointer to the name of the validating domain controller, in NetBIOS format.
    /// If this member is NULL, the Windows NT 4.0-style policy will not be applied.
    ///
    public string lpServerName;

    ///
    /// Pointer to the path of the Windows NT 4.0-style policy file. 
    /// This member can be NULL.
    ///
    public string lpPolicyPath;

    ///
    /// Handle to the HKEY_CURRENT_USER registry key.
    ///
    public IntPtr hProfile;
}

I hope it helps you.
Regards,
Pavan N

Comments

Popular posts from this blog

Download file using WCF REST services

server side printing / PDF conversions using c# and asp.net/wcf service

Finding file size in bytes on serverside using c#