views:

52

answers:

2

if using code like the following to impersonate another user,

[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool
    LogonUser(string lpszUsername, string lpszDomain,
        string lpszPassword, int dwLogonType,
        int dwLogonProvider, ref IntPtr phToken);
var handle = IntPtr.Zero;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int SecurityImpersonation = 2;
LogonUser(username, domain,
      password, LOGON32_LOGON_NETWORK,
      LOGON32_PROVIDER_DEFAULT, ref handle))

on two different concurrent threads, will they interfere with one another? I.e., is the currently logged-on user associated with the thread, or with the host process ??

EDIT: Addition...

I am using the logon handle to create a WindowsImpersonationContext object, as a private state field in an instance of a type I named "Impersonator" (code below). So, since this WindowsImpersonationContext object is a local provate field in an imnstance of thos type, and a new instance of this type is created each time I wanto impersonate some set of credentials, I can assume that this WindowsImpersonationContext is what is being used to perform all ACL validations during execution of code within a block such as

   using (Impersonator.Impersonate(userId, domain, password))
   {
       // code I want to execute using supplied credentials
   }

What has me concerned is the statement on the msdn page WindowsImpersonationContext that says: "Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe."

Impersonator class:

public class Impersonator: IDisposable
    {
        #region Declarations
        private readonly string username;
        private readonly string password;
        private readonly string domain;
        // this will hold the security context 
        // for reverting back to the client after
        // impersonation operations are complete
        private WindowsImpersonationContext impersonationContext;
        #endregion Declarations

        #region Constructors

        public Impersonator(string UserName, 
            string Domain, string Password)
        {
            username = UserName;
            domain = Domain;
            password = Password;
        }
        #endregion Constructors

        #region Public Methods
        public static Impersonator Impersonate(
            string userName, string domain, string password)
        {
            var imp = new Impersonator(userName, domain, password);
            imp.Impersonate();
            return imp;
        }
        public void Impersonate()
        { impersonationContext = Logon().Impersonate(); }
        public void Undo() { impersonationContext.Undo(); }
        #endregion Public Methods

        #region Private Methods
        private WindowsIdentity Logon()
        {
            var handle = IntPtr.Zero;

            const int LOGON32_LOGON_NETWORK = 3;
            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int SecurityImpersonation = 2;

            // attempt to authenticate domain user account
            try
            {
                if (!LogonUser(username, domain,
                    password, LOGON32_LOGON_NETWORK,
                    LOGON32_PROVIDER_DEFAULT, ref handle))
                    throw new LogonException(
                        "User logon failed. Error Number: " +
                        Marshal.GetLastWin32Error());

                // ----------------------------------
                var dupHandle = IntPtr.Zero;
                if (!DuplicateToken(handle,
                    SecurityImpersonation,
                    ref dupHandle))
                    throw new LogonException(
                        "Logon failed attemting to duplicate handle");
                // Logon Succeeded ! return new WindowsIdentity instance
                return (new WindowsIdentity(handle));
            }
            // close the open handle to the authenticated account
            finally { CloseHandle(handle); }
        }

        #region external Win32 API functions
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool
            LogonUser(string lpszUsername, string lpszDomain,
                    string lpszPassword, int dwLogonType,
                    int dwLogonProvider, ref IntPtr phToken);
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool CloseHandle(IntPtr handle);
        // --------------------------------------------

        [DllImport("advapi32.dll", CharSet = CharSet.Auto,
             SetLastError = true)]
        public static extern bool DuplicateToken(
            IntPtr ExistingTokenHandle,
            int SECURITY_IMPERSONATION_LEVEL,
            ref IntPtr DuplicateTokenHandle);
        // --------------------------------------------
        #endregion external Win32 API functions
        #endregion Private Methods

        #region IDisposable
        private bool disposed;
        public void Dispose() { Dispose(true); }
        public void Dispose(bool isDisposing)
        {
            if (disposed) return;
            if (isDisposing) Undo();
            // -----------------
            disposed = true;
            GC.SuppressFinalize(this);
        }
        ~Impersonator() { Dispose(false); }

        #endregion IDisposable
    }
+2  A: 

It's not associated with anything. The handle that you have is a logon handle. Once you have that, you use that handle to impersonate the user on a thread (using WindowsIdentity.Impersonate) or for a process (either through the CreateProcess API function or by impersonating on the thread and then creating a new Process instance while impersonating a user).

Either way, calling LogonUser doesn't perform any of the impersonation, it simply gives you a user handle which you need to perform impersonation (assuming that you have the privilege).

casperOne
Right, I use that handle to create an WindowsImpersonationContext object, that IS thread-safe, (it is an instance of a defined type created one per use). By "associated", I meant where is that logon handle stored? As I understand it, it is this WindowsImpersonationContext object that "holds" the reference to the security context, and if this is not shared across threads I should be ok... right ?
Charles Bretana
What has me concerned is the statement on the msdn page http://msdn.microsoft.com/en-us/library/system.security.principal.windowsimpersonationcontext.aspx that says: "Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe."
Charles Bretana
@Charles Bretana: The instance members of WindowsImpersonationContext instance are NOT thread-safe. You can not make calls to the same instance from multiple threads with guaranteed safety. This object is like most others in .NET, instance members are not thread-safe, static members are. As for where the login handle is stored, the handle is in the IntPtr, which points to the information for the login token.
casperOne
@Charles Bretana: When you create a WindowsImpersonationContext, that's only for the current thread. You impersonate on the current thread. I would not pass around those instances to other threads or make them accessible to other threads. Create your context and impersonate, do your work, and then revert to the original user.
casperOne
@Casper, thanks, Then in my case, as I am calliung this code in a using block, and calling Undo() in the Dispose(), I should be safe.
Charles Bretana
A: 

I think you mean . If thread A impersonates Joe and at the same time thread B impersonates Fred will all work OK. Ie will thread A work as Joe (and not fred) and vice versa. The answer is yes; the impersonation belongs to the thread

pm100