Question

I have been using this link to setup impersonation. I have many methods and i need to impersonate for all of them.

  1. Do i have to add the using code around every method call or is there a way to set impersonation for the entire lifecycle of the application ?
  2. I am impersonating a local administrator below - are there any flaws or security implications with my code ?
  3. is the LOGON32_LOGON_NEW_CREDENTIALS value of 2 correct below (it is working as expected)

c# Code

private void buttonUsingImpersonation_Click(object sender, EventArgs e)
        {
            try
            {               
                using (new Impersonation("LocalHost", "test", "test"))
                {
                    // do whatever you want

                    string fileName = System.IO.Path.GetRandomFileName();
                    String pathString = System.IO.Path.Combine(FolderPath, fileName);
. 
                    if (!System.IO.File.Exists(pathString))
                    {
                        using (System.IO.FileStream fs = System.IO.File.Create(pathString))
                        {
                            for (byte i = 0; i < 100; i++)
                            {
                                fs.WriteByte(i);
                            }
                        }
                    }
                    else
                    {
                        MessageBox.Show("File already exists " + fileName);

                        return;
                    }
                }
            }
            catch (Exception ex)
            {
                // If exception happens, it will be returned here
                MessageBox.Show("Error " + MethodBase.GetCurrentMethod().Name + " " + ex.ToString());
            }
        }

Class

using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using Microsoft.Win32.SafeHandles;

namespace ImpersonationDemo
{
    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    public class Impersonation : IDisposable
    {
        private readonly SafeTokenHandle _handle;
        private readonly WindowsImpersonationContext _context;

        const int LOGON32_LOGON_NEW_CREDENTIALS = 2;

        public Impersonation(string domain, string username, string password)
        {
            var ok = LogonUser(username, domain, password,
                           LOGON32_LOGON_NEW_CREDENTIALS, 0, out this._handle);
            if (!ok)
            {
                var errorCode = Marshal.GetLastWin32Error();
                throw new ApplicationException(string.Format("Could not impersonate the elevated user.  LogonUser returned error code {0}.", errorCode));
            }

            this._context = WindowsIdentity.Impersonate(this._handle.DangerousGetHandle());
        }

        public void Dispose()
        {
            this._context.Dispose();
            this._handle.Dispose();
        }

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);

        public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            private SafeTokenHandle()
                : base(true) { }

            [DllImport("kernel32.dll")]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            [SuppressUnmanagedCodeSecurity]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool CloseHandle(IntPtr handle);

            protected override bool ReleaseHandle()
            {
                return CloseHandle(handle);
            }
        }
    }
}
Was it helpful?

Solution

There are many ways of doing this, not really a single correct way. One way could be that you pass the methods you want impersonated as a delegate, to some kind of ImpersonatedContext class, that will wrap the invocation in your Impersonator scope. This will also move all the logic away from the button click event handler, which is also architecturally not the best way.

Regarding your question about security, then as of now you are specifying the username and password directly. If Eve was to decompile your code, she would be able to see the password. Maybe you should let the user specify administrators password?

I have been using the example code as well, and it has worked fine for me. I would think that LOGON32_LOGON_NEW_CREDENTIALS is fine. You can read more about the different modes here (LogonUser)

Update I guess a quick sample of what that would look like, is something like this. The delegate you pass accepts an object, and returns a success value as a boolean. You could modify that to suit your needs.

public class ImpersonationContext {
    public delegate bool ImpersonationDel(object obj);

    public bool Invoke(ImpersonationDel del){
        using (new Impersonation("LocalHost", "test", "test")){
            return del.Invoke();
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top