Impersonate a Windows Identity Easily!

Not Me

Why would you want to do Windows Identity impersonation?

Because it’s cool. Really. You can run a program as someone other than who you are. This is especially useful for services, websites, and pretty much anything where you want to run an application in a mode that has different privileges. Do you want more permissions than normal? Use an application in a domain? Lock it down? The sky is the limit!

How is it done?

That part is pretty simple. In fact it’s outlined on this MSDN page. I’ve also got it wrapped up in a much easier to use class here:

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 ConsoleApplication1
{
    /// Impersonates a windows identity.
    /// Based on: http://msdn.microsoft.com/en-us/library/w070t6ka.aspx
    public class WindowsIdentityImpersonator : IDisposable
    {
        WindowsIdentity _newId;
        SafeTokenHandle _safeTokenHandle;
        WindowsImpersonationContext _impersonatedUser;
 
        public WindowsIdentity Identity { get { return _newId; } }
 
        [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
        public WindowsIdentityImpersonator(string Domain, string Username, string Password)
        {
            bool returnValue = LogonUser(Username, Domain, Password, 2, 0, out _safeTokenHandle);
 
            if (returnValue == false)
            {
                throw new UnauthorizedAccessException("Could not login as " + Domain + "\\" + Username + ".",
                    new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()));
            }
        }
 
        public void BeginImpersonate()
        {
            _newId = new WindowsIdentity(_safeTokenHandle.DangerousGetHandle());
            _impersonatedUser = _newId.Impersonate();
        }
 
        public void EndImpersonate()
        {
            if (_newId != null)
            {
                _newId.Dispose();
            }
 
            if (_impersonatedUser != null)
            {
                _impersonatedUser.Dispose();
            }
        }
 
        public void Dispose()
        {
            this.EndImpersonate();
 
            if (_safeTokenHandle != null)
            {
                _safeTokenHandle.Dispose();
            }
        }
 
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
            int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
 
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);
    }
 
    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);
        }
    }
}

Using this class is simple:

using (var wim = new WindowsIdentityImpersonator("domain", "username", "password"))
{
    wim.BeginImpersonate();
    {
        Console.WriteLine("Thread B: {0}", WindowsIdentity.GetCurrent().Name);
    }
    wim.EndImpersonate();
}

For domain if you want to use your local machine just put in “.”

Ok, that’s cool but what else have you got?

You want more? Okay wise guy, throw this bad boy in a separate thread (even a ThreadPool will work) and you can run that thread under this identity. Want another one? Start another thread. It’s that easy! One application can perform actions under a hundred different identities!

Happy coding!

Cache Control for Fun and Profit

cache_control

So you have a website and it’s really important. Maybe people are using it publicly and entering credit cards or something. What’s the worst thing that can happen? Well I’ll tell you, the next guy could come up and hit the back button and see everyone’s credit card numbers. Think it’s not plausible? I once went to the DMV near me and found a kiosk to renew my driver’s license. It was just a computer hooked up to the internet in a very poor kiosk mode. So the first thing I did was hit the unmarked backspace key and saw the last guy’s credit card number. Why did that happen? Improper cache-control!!! Sorry, that just came out.

I digress.

So you how you do it in .NET? I mean, we want to cache some things, but not others. Well it’s pretty easy, but in practice there are a few tricks. First, we want to cache static content (pictures, JavaScript files, etc) but we DO NOT want to cache our pages. So let’s setup static content caching in our web.config at the configuration level:

<staticContent>
  <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
</staticContent>

That was easy enough, and it works! But we still have some other content (the content we want secured) marked as “private” in the caching, that’s bad. So we’ll fix that by using the powers of Global.asax.cs!

protected void Application_EndRequest(Object sender, EventArgs e)
{
  if ((Response.Headers["Cache-Control"] ?? "") == "private")
  {
    // Stop Caching in IE
    Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
    // Stop Caching in Firefox
    Response.Cache.SetNoStore();
  }
}

So we are now checking for things that are marked “private” cache and making them expire immediately. But why am I not using Response.CacheControl? It turns out that Response.CacheControl is not as responsive as I would like:

Not the same

As you can see, Response.CacheControl thinks the static content is “private” where in reality it is “max-age”. Boo. But by directly checking the Response.Headers we will find the correct value and can check for it. This results in epic win!

PropaCaching

 

Enjoy!