Still getting duplicate token error after calling DuplicateTokenEx for impersonated token
- by atconway
I'm trying to return a Sytem.IntPtr from a service call so that the client can use impersonation to call some code. My imersonation code works properly if not passing the token back from a WCF service. I'm not sure why this is not working. I get the following error: 
  "Invalid token for impersonation - it cannot be duplicated."
Here is my code that does work except when I try to pass the token back from a service to a WinForm C# client to then impersonate.
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
public extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType, int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);
private IntPtr tokenHandle = new IntPtr(0);
private IntPtr dupeTokenHandle = new IntPtr(0);
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
    public int Length;
    public IntPtr lpSecurityDescriptor;
    public bool bInheritHandle;
}
public enum SecurityImpersonationLevel
{
    SecurityAnonymous = 0,
    SecurityIdentification = 1,
    SecurityImpersonation = 2,
    SecurityDelegation = 3
}
public enum TokenType
{
    TokenPrimary = 1,
    TokenImpersonation = 2
}
private const int MAXIMUM_ALLOWED = 0x2000000;
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public System.IntPtr GetWindowsUserToken(string UserName, string Password, string DomainName)
{
    IntPtr tokenHandle = new IntPtr(0);
    IntPtr dupTokenHandle = new IntPtr(0);
    const int LOGON32_PROVIDER_DEFAULT = 0;
    //This parameter causes LogonUser to create a primary token.
    const int LOGON32_LOGON_INTERACTIVE = 2;
    //Initialize the token handle            
    tokenHandle = IntPtr.Zero;
    //Call LogonUser to obtain a handle to an access token for credentials supplied.
    bool returnValue = LogonUser(UserName, DomainName, Password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref tokenHandle);        
    //Make sure a token was returned; if no populate the ResultCode and throw an exception:
    int ResultCode = 0;
    if (false == returnValue)
    {
        ResultCode = Marshal.GetLastWin32Error();
        throw new System.ComponentModel.Win32Exception(ResultCode, "API call to LogonUser failed with error code : " + ResultCode);
    }
    SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
    sa.bInheritHandle = true;
    sa.Length = Marshal.SizeOf(sa);
    sa.lpSecurityDescriptor = (IntPtr)0;
    bool dupReturnValue = DuplicateTokenEx(tokenHandle, MAXIMUM_ALLOWED, ref sa, (int)SecurityImpersonationLevel.SecurityDelegation, (int)TokenType.TokenImpersonation, ref dupTokenHandle);
    int ResultCodeDup = 0;
    if (false == dupReturnValue)
    {
        ResultCodeDup = Marshal.GetLastWin32Error();
        throw new System.ComponentModel.Win32Exception(ResultCode, "API call to DuplicateToken failed with error code : " + ResultCode);
    }
    //Return the user token
    return dupTokenHandle;
}
Any idea if I'm not using the call to DuplicateTokenEx correctly? According to the MSDN documentation I read here I should be able to create a token valid for delegation and use across the context on remote systems. When 'SecurityDelegation' is used, the server process can impersonate the client's security context on remote systems. 
Thanks!