0

I'm trying to impersonate a user account in a .NET console application and when it reaches the point where I call the Impersonate() method, the application seems to exit instantly. The method call is in a try-catch block, but it never even reaches the 'catch' block so I can't see what the error was!

Here's the relevant code:

[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

And then at the start of my application:

bool returnValue = LogonUser(user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref m_tokenHandle);
if (!returnValue)
{
    int ret = Marshal.GetLastWin32Error();
    throw new System.ComponentModel.Win32Exception(ret);
}
// Impersonate
WindowsIdentity _identity = new WindowsIdentity(m_tokenHandle);
EmailHelper.SendErrorMessage("Windows Identity Created!", string.Format("Created at: {0}", DateTime.Now.ToLongTimeString()));
try
{
    m_impersonatedUser = _identity.Impersonate();
    EmailHelper.SendErrorMessage("Impersonation Complete...", string.Format("Impersonation at: {0}", DateTime.Now.ToLongTimeString()));
}
catch
{
    int ret = Marshal.GetLastWin32Error();
    EmailHelper.SendErrorMessage("Error impersonating user!", string.Format("Error: {0}", ret));
}
finally
{
    EmailHelper.SendErrorMessage("Finally block executed?!", "What is going on?");
}

I'm getting the initial "Windows identity created" email but not any of the others! Does anyone know how I can find out what my underlying error is and why my impersonation isn't working?

Thanks!

EDIT:

I tried the code from this MSDN article: http://msdn.microsoft.com/en-us/library/w070t6ka(v=vs.110).aspx with the only modification being trying to create a file when impersonating.

The modification was changing this code:

using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
{
    // Check the identity.
    Console.WriteLine("After impersonation: " + WindowsIdentity.GetCurrent().Name);
}

To:

using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
{
    // Check the identity.
    Console.WriteLine("After impersonation: " + WindowsIdentity.GetCurrent().Name);
    System.IO.File.CreateText(@"Test.txt");
}

The output I am getting when running the exe as a "standard" user says that LogonUser succeeded, WindowsIdentity.GetCurrent().Name returns the impersonated user name but creating the file fails with error code 1346, which, according to MSDN, is:

ERROR_BAD_IMPERSONATION_LEVEL 1346 (0x542) Either a required impersonation level was not provided, or the provided impersonation level is invalid.

What does that mean? Google hasn't been able to shed any light on it yet...

EDIT 2:

I can impersonate another standard user account using this code, but when I try to impersonate my admin account it fails. Is it possible to change the code somehow to allow a standard user running this exe to impersonate an admin account?

dasboth
  • 216
  • 3
  • 12
  • Please show some code to reveal what is "create a file inside the using". If you write to a wrong location, or use the wrong code, then the error code is not strange. – Lex Li May 22 '14 at 08:57
  • Hi Lex Li, I edited my question to show the code that I added. When trying to impersonate an admin account, the application does not create the file, but writes the "after impersonation" line to the console. – dasboth May 22 '14 at 09:06
  • @dasboth: Can you log out of **THAT** PC and Login using the exact Username/Password that you have specified in your code? (i.e. `domain\user` and `password`) It sounds like you are trying to log in with an account that does not exist on the PC yet. (???) –  May 22 '14 at 14:55
  • The account I'm running Visual Studio with is my admin account, which is the one logged in by default and the the one I'm trying to impersonate. I then switch user and login as my "standard" user, try to do the impersonation, which then fails with the "ERROR_BAD_IMPERSONATION_LEVEL" error. I tried different combinations of login types and impersonation levels, all to no avail. Is it possible that impersonating an admin account is not supported? – dasboth May 22 '14 at 15:38

3 Answers3

0

When I ran your code, I replaced all the email sending calls with Console.WriteLine and created the Windows Identity with my UPN. This resulted in a call to the try block, then the finally block, as expected.

There could be an issue with your email sending method. I would see if you get the same results I did after replacing that with Console.WriteLine(). Also try adding in a Console.ReadLine() at the end so you can see what's happening without the application exiting. It could be that this is working properly, and you're just not getting the emails. If the emails were working properly, the application would exit instantly if there are no lines to call after that one, but you'd still get an email.

eddie_cat
  • 2,527
  • 4
  • 25
  • 43
  • Hi Savanna, thanks for testing it on your end. I should give a bit of context: the purpose of this code is to copy some files to the local machine from a server, which is otherwise inaccessible to users, hence why I need the impersonation to copy the files as an admin. Initially the email code wasn't there and it was writing to a log file instead but I replaced it with the email code because I thought it was a permissions issue. Either way, running the code in Visual Studio works but deploying it and running the executable as a user is when I get the behaviour I described. – dasboth May 21 '14 at 14:33
  • Ran out of characters in my previous comment. When I run the executable as a user, I get the initial email but none of the others, not even the one in the finally block. – dasboth May 21 '14 at 14:36
  • The log file was working, but again only when running in Visual Studio. Running the exe as a user did not even create a log file, which is why I replaced it with email code thinking it was a permissions issue. The code I posted is in a class library, and the executable I'm running literally does nothing but call the method in that class library. Could that make a difference? When you say your UPN, what do you mean? LogonUser is returning true, does that not mean it's authenticated it correctly? – dasboth May 21 '14 at 14:38
  • You can create WindowsIdentity in a few ways. http://msdn.microsoft.com/en-us/library/system.security.principal.windowsidentity.windowsidentity(v=vs.110).aspx I created this one with a string that represents my own user name. So I was impersonating myself. I'm not sure what m_tokenHandle is for you. – eddie_cat May 21 '14 at 14:44
  • I'm guessing the token handle is valid, because the LogonUser returns a success (I can see a login success in my Event Viewer as well). I just tried using DuplicateToken to generate a different token and use that to create the WindowsIdentity and that seems to have done exactly the same thing. – dasboth May 21 '14 at 14:52
  • Still nothing, I added the SafeTokenHandle code from the example you linked to and it still gets to the point of instantiating the WindowsIdentity but when I call Impersonate() it just crashes without warning and without continuing through the code. Running this code in Visual Studio still works and sends all the emails I'm expecting! Is there any reason the application would exit completely when running as a "standard" user? – dasboth May 21 '14 at 15:01
0

Have you tested m_tokenHandle?

if (m_tokenHandle != IntPtr.Zero)
{
  // continue on

Have you tested _identity? There is no test for it either.

if (_identity != null)
{
  // continue on

The finally block will execute whether the try routine succeeds or not.

  • Hi jp2code, I tried adding some code to send me an email about the values and m_tokenHandle is not equal to IntPtr.Zero and _identity isn't null either. The code works (gets past the try-catch) when running in Visual Studio (which runs under my admin account) but not when executing as a user, which is where I think the issue is but the code in the finally block doesn't execute because the application doesn't seem to get that far! – dasboth May 21 '14 at 14:44
  • You may need to have your network guys set you up an Admin Account for your PC. Then, impersonate that account before calling your code. See [How do you do Impersonation in .NET?](http://stackoverflow.com/a/7250145/153923). –  May 21 '14 at 18:22
  • The account I'm impersonating is an admin account but the account running the exe is a standard user account. Maybe the user account needs some extra permissions to allow my code to impersonate the admin account? – dasboth May 22 '14 at 08:48
0

Sadly this still doesn't seem to work and I have a hunch that trying to impersonate an admin account using WindowsIdentity may not be supported, or my approach is flawed. Either way, rather anticlimactically, I've decided to go for a different approach entirely and put the code I want to run as an admin into a separate executable, and then started that exe as a new process with the admin credentials.

System.Diagnostics.Process.Start(myExecutablePath, args, adminUsername, adminPassword, domainName);

This works fine, so perhaps anyone encountering this issue might be able to use this workaround as well.

dasboth
  • 216
  • 3
  • 12