Fri
Apr 20
2007

Impersonate User

/// <summary>
/// Impersonate a user within the current thread.
/// This class will automatically revert the user to the original user 
/// identity when this class is disposed. Use this class with the 
/// C# using or a try/catch block to ensure the user is reverted
/// after an exception.
/// 
/// Suggested usage:
/// using (new ImpersonateHelper("domain", "joe", "password"))
/// {
///     ...code to run as joe
/// }
/// ...code to run as orignial user
/// 
/// Note, this implementation is vulnerable to "exception filter" attacks
/// as described below. Evaluate it against the risk of other attacks
/// such as the credential store used to impersonate.
/// http://blogs.msdn.com/shawnfa/archive/2005/03/22/400749.aspx
/// </summary>
public class ImpersonationHelper : IDisposable
{
    IntPtr m_tokenHandle = new IntPtr(0);
    WindowsImpersonationContext m_impersonatedUser;
 
    #region Win32 API Declarations
 
    const int LOGON32_PROVIDER_DEFAULT = 0;
    const int LOGON32_LOGON_INTERACTIVE = 2;    //This parameter causes LogonUser to create a primary token.
 
    [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);
 
    [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, 
    int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
 
    #endregion
 
    /// <summary>
    /// Constructor. Impersonates the requested user. Impersonation lasts until
    /// the instance is disposed.
    /// </summary>
    public ImpersonationHelper(string domain, string user, string password)
    {
        // Call LogonUser to obtain a handle to an access token.
        bool returnValue = LogonUser(user, domain, password,
            LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
            ref m_tokenHandle);
        if (false == returnValue)
        {
            int ret = Marshal.GetLastWin32Error();
            throw new System.ComponentModel.Win32Exception(ret);
        }
 
        // Impersonate
        m_impersonatedUser = new WindowsIdentity(m_tokenHandle).Impersonate();
    }
 
    #region IDisposable Pattern
 
    /// <summary>
    /// Revert to original user and cleanup.
    /// </summary>
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Revert to original user identity
            if (m_impersonatedUser != null)
                m_impersonatedUser.Undo();
        }
 
        // Free the tokens.
        if (m_tokenHandle != IntPtr.Zero)
            CloseHandle(m_tokenHandle);
    }
 
    /// <summary>
    /// Explicit dispose.
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
 
    /// <summary>
    /// Destructor
    /// </summary>
    ~ImpersonationHelper()
    {
        Dispose(false);
    }
 
    #endregion
}
Tue
Apr 17
2007

Log4Net + ASP.Net

In web applications, Log4Net’s ThreadContext properties (typically items like the current user) should be set immediately before each logging call.

Normally these properties are set in Global.Session_Start(). However, log4net internally stores these properties in thread local storage. Using Reflector to disassemble log4net.dll (v1.2.10), start with log4net.ThreadContext which leads to ThreadContextProperties.get_Item() and then to ThreadContextProperties.GetProperties(). The disassembly for this method:

internal PropertiesDictionary GetProperties(bool create)
{
    PropertiesDictionary data;
    data = (PropertiesDictionary) Thread.GetData(s_threadLocalSlot);
    if ((data == null) && create)
    {
        data = new PropertiesDictionary();
        Thread.SetData(s_threadLocalSlot, data);
    }
    return data;
}

The call to Thread.GetData() is the problem. ASP.Net, when under load, may switch threads when processing a single request (see ThreadStatic, CallContext and HttpContext in ASP.Net). You may initialize log4net with the current user on one thread, then attempt to log on a different thread. Log4net.LogicalThreadContext() suffers a similar problem as it uses CallContext to store its data rather than HttpContext.

For reliable logging information, set these ThreadContext properties each time you log an entry. The additional processing time will be negligable in most cases. Further, as a general rule of thumb, use HttpContext to store request-specific information in ASP.Net applications.

10 minutes after finishing this post I come across this post, also from piers7:
log4net Context problems with ASP.Net thread agility. It suggests another workaround.

Wed
Apr 11
2007

General Tools

Dependency Walker
Determines external dependencies (down to the function level) on Windows exe’s, dll’s, etc…
XPath Expression Testbed
FileHelpers
Create strongly typed flat file reader/writers
Unlocker

© 2009 Brian Low. All rights reserved.