views:

649

answers:

2

I'm using the LogonUser win32 api:

token = LogonUser(...)
WindowsIdentity newId = new WindowsIdentity(token);            
WindowsImpersonationContext impersonatedUser = newId.Impersonate();

However when calling a WCF service after this I'm not able to use the impersonated identity. I think this is because impersonatedUser.ImpersonationLevel equals Impersonation.

Is this the reason? Is a level of ImpersonationLevel.Identification what I need? How to get such a level?

+2  A: 

I don't know if this will work for WCF. But we use it in our production web app for impersonation to read and write files to the file system. You will need to define the API's for AdvApi32.LogonUser, AdvApi32.DuplicateToken, and Kernel32.CloseHandle and make sure to Close the WindowsImpersonationContext when you are done.

 /// <summary>impersonates a user</summary>
 /// <param name="username">domain\name of the user account</param>
 /// <param name="password">the user's password</param>
 /// <returns>the new WindowsImpersonationContext</returns>
 public static WindowsImpersonationContext ImpersonateUser(String username, String password)
 {
  WindowsIdentity winId = WindowsIdentity.GetCurrent();
  if (winId != null)
  {
   if (string.Compare(winId.Name, username, true) == 0)
   {
    return null;
   }
  }

  //define the handles
  IntPtr existingTokenHandle = IntPtr.Zero;
  IntPtr duplicateTokenHandle = IntPtr.Zero;

  String domain;
  if (username.IndexOf("\\") > 0)
  {
   //split domain and name
   String[] splitUserName = username.Split('\\');
   domain = splitUserName[0];
   username = splitUserName[1];
  }
  else
  {
   domain = String.Empty;
  }

  try
  {
   //get a security token

   bool isOkay = AdvApi32.LogonUser(username, domain, password,
    (int) AdvApi32.LogonTypes.LOGON32_LOGON_INTERACTIVE,
    (int) AdvApi32.LogonTypes.LOGON32_PROVIDER_DEFAULT,
    ref existingTokenHandle);

   if (!isOkay)
   {
    int lastWin32Error = Marshal.GetLastWin32Error();
    int lastError = Kernel32.GetLastError();

    throw new Exception("LogonUser Failed: " + lastWin32Error + " - " + lastError);
   }

   // copy the token

   isOkay = AdvApi32.DuplicateToken(existingTokenHandle,
    (int) AdvApi32.SecurityImpersonationLevel.SecurityImpersonation,
    ref duplicateTokenHandle);

   if (!isOkay)
   {
    int lastWin32Error = Marshal.GetLastWin32Error();
    int lastError = Kernel32.GetLastError();
    Kernel32.CloseHandle(existingTokenHandle);
    throw new Exception("DuplicateToken Failed: " + lastWin32Error + " - " + lastError);
   }
   // create an identity from the token

   WindowsIdentity newId = new WindowsIdentity(duplicateTokenHandle);
   WindowsImpersonationContext impersonatedUser = newId.Impersonate();

   return impersonatedUser;
  }
  finally
  {
   //free all handles
   if (existingTokenHandle != IntPtr.Zero)
   {
    Kernel32.CloseHandle(existingTokenHandle);
   }
   if (duplicateTokenHandle != IntPtr.Zero)
   {
    Kernel32.CloseHandle(duplicateTokenHandle);
   }
  }
 }
consultutah
+1  A: 

after this I'm not able to use the impersonated identity

The impersonation should be effective for access on the same box, but not on the network.

It may be, as consultutah's code shows, that you just need to call DuplicateToken() in order to convert the logon token to an impersonation token before it can be used.

I think this is because impersonatedUser.ImpersonationLevel equals Impersonation.

If you need to act as the impersonated user to other systems, you need a higher level of impersonation called 'delegation'. This is basically equivalent to having the user's password so you can represent yourself as them to others.

Marsh Ray