WindowsIdentity.Impersonate() vs ImpersonateLoggedOnUser()

G

Guest

After playing with the code shown and utilising Willy Denyottes' help, I have
come to the conclusion that there is some form of difference between the
managed WindowsIdentity.Impersonate() over the unmanaged
ImpersonateLoggedOnUser().

Below is my code showing a file copy to a remote computer's shared folder
using both WindowsImpersonationContext and
ImpersonateLoggedOnUser/RevertToSelf, with the latter currently commented out.

The issue I have is that the code executes and copies the file when using
ImpersonateLoggedOnUser, but not when using a WindowsImpersonationContext.
This wouldn't be an issue except that the ImpersonateLoggedOnUser does not
work when called on Windows 2000/2003 Pro/Server, only Windows XP.

The question would be why is this so and what can I do to get it working
with managed code. Note that the logon type and provider listed here are only
test values that I'm using, but no combination yields any better result other
than error 1326 "Logon failure: unknown user name or bad password".

Thanks.


using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;

namespace SecurityTest
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class SecurityTest
{
[DllImport("advapi32.DLL", SetLastError = true)]
public static extern int LogonUser(string lpszUsername, string lpszDomain,
string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr
phToken);
[DllImport("advapi32.DLL")]
public static extern bool ImpersonateLoggedOnUser(IntPtr hToken); //
handle to token for logged-on user
[DllImport("advapi32.DLL")]
public static extern bool RevertToSelf();

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
IntPtr admin_token;

//WindowsIdentity wid_current = WindowsIdentity.GetCurrent();

WindowsIdentity wid_admin = null;
WindowsImpersonationContext wic = null;

try
{
Console.WriteLine("Copying file...");
if (LogonUser("Administrator", "192.168.0.1", "password", 9, 0, out
admin_token) != 0)
{
//ImpersonateLoggedOnUser(admin_token);
wid_admin = new WindowsIdentity(admin_token);
wic = wid_admin.Impersonate();
System.IO.File.Copy("C:\\rpmtest.txt",
"\\\\192.168.0.1\\bb.uploads\\test.txt", true);
Console.WriteLine("Copy succeeded");
}
else Console.WriteLine("Copy Failed");
}
catch (System.Exception se)
{
int ret = Marshal.GetLastWin32Error();
Console.WriteLine(ret.ToString(), "Error code: " + ret.ToString());
Console.WriteLine(se.Message);
}
finally
{
//RevertToSelf();
if (wic != null) wic.Undo();
}

//Console.ReadLine();
}
}
}
 
W

Willy Denoyette [MVP]

Windows 2000 uses NTLM as authentication protocol provider, but Logontype 9
needs Kerberos authentication, so you must explicitely specify
LOGON32_PROVIDER_WINNT50 for this to work on W2K.

// 9 = LOGON32_LOGON_NEW_CREDENTIALS
// 5 = LOGON32_PROVIDER_WINNT50
if(LogonUser(userName, domain, password, 9, 3, ref token) != 0)
{
....
Note that your code leaks a handle, whenener you call LogonUser you have to
close the handle when done with it!

[DllImport("kernel32.dll")]
public extern static bool CloseHandle(IntPtr hToken);


impersonationContext = tempWindowsIdentity.Impersonate();
// close impersonation token, no longer needed
CloseHandle(token);

Willy.


BLiTZWiNG said:
After playing with the code shown and utilising Willy Denyottes' help, I
have
come to the conclusion that there is some form of difference between the
managed WindowsIdentity.Impersonate() over the unmanaged
ImpersonateLoggedOnUser().

Below is my code showing a file copy to a remote computer's shared folder
using both WindowsImpersonationContext and
ImpersonateLoggedOnUser/RevertToSelf, with the latter currently commented
out.

The issue I have is that the code executes and copies the file when using
ImpersonateLoggedOnUser, but not when using a WindowsImpersonationContext.
This wouldn't be an issue except that the ImpersonateLoggedOnUser does not
work when called on Windows 2000/2003 Pro/Server, only Windows XP.

The question would be why is this so and what can I do to get it working
with managed code. Note that the logon type and provider listed here are
only
test values that I'm using, but no combination yields any better result
other
than error 1326 "Logon failure: unknown user name or bad password".

Thanks.


using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;

namespace SecurityTest
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class SecurityTest
{
[DllImport("advapi32.DLL", SetLastError = true)]
public static extern int LogonUser(string lpszUsername, string lpszDomain,
string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr
phToken);
[DllImport("advapi32.DLL")]
public static extern bool ImpersonateLoggedOnUser(IntPtr hToken); //
handle to token for logged-on user
[DllImport("advapi32.DLL")]
public static extern bool RevertToSelf();

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
IntPtr admin_token;

//WindowsIdentity wid_current = WindowsIdentity.GetCurrent();

WindowsIdentity wid_admin = null;
WindowsImpersonationContext wic = null;

try
{
Console.WriteLine("Copying file...");
if (LogonUser("Administrator", "192.168.0.1", "password", 9, 0, out
admin_token) != 0)
{
//ImpersonateLoggedOnUser(admin_token);
wid_admin = new WindowsIdentity(admin_token);
wic = wid_admin.Impersonate();
System.IO.File.Copy("C:\\rpmtest.txt",
"\\\\192.168.0.1\\bb.uploads\\test.txt", true);
Console.WriteLine("Copy succeeded");
}
else Console.WriteLine("Copy Failed");
}
catch (System.Exception se)
{
int ret = Marshal.GetLastWin32Error();
Console.WriteLine(ret.ToString(), "Error code: " + ret.ToString());
Console.WriteLine(se.Message);
}
finally
{
//RevertToSelf();
if (wic != null) wic.Undo();
}

//Console.ReadLine();
}
}
}
 
G

Guest

Ok, I remember you said this from your last post, and I've been trying to get
it to work from there. Winbase.h defines LOGON32_PROVIDER_WINNT50 as 3. If I
put 5 in I get error 87 (invalid parameter). 3 gives me the same error as 0
(error 1326) when using managed code, and no error (and successful copy) when
using unmanaged code (but only on XP). I get the same errors using 8 and 3 in
managed code also (LOGON32_LOGON_NETWORK_CLEARTEXT and LOGON32_LOGON_NETWORK).

This is the bit that has me confused. For all intents and purposes it
appears to work in XP but only with unmanaged code.

Willy Denoyette said:
Windows 2000 uses NTLM as authentication protocol provider, but Logontype 9
needs Kerberos authentication, so you must explicitely specify
LOGON32_PROVIDER_WINNT50 for this to work on W2K.

// 9 = LOGON32_LOGON_NEW_CREDENTIALS
// 5 = LOGON32_PROVIDER_WINNT50
if(LogonUser(userName, domain, password, 9, 3, ref token) != 0)
{
....
Note that your code leaks a handle, whenener you call LogonUser you have to
close the handle when done with it!

[DllImport("kernel32.dll")]
public extern static bool CloseHandle(IntPtr hToken);


impersonationContext = tempWindowsIdentity.Impersonate();
// close impersonation token, no longer needed
CloseHandle(token);

Willy.


BLiTZWiNG said:
After playing with the code shown and utilising Willy Denyottes' help, I
have
come to the conclusion that there is some form of difference between the
managed WindowsIdentity.Impersonate() over the unmanaged
ImpersonateLoggedOnUser().

Below is my code showing a file copy to a remote computer's shared folder
using both WindowsImpersonationContext and
ImpersonateLoggedOnUser/RevertToSelf, with the latter currently commented
out.

The issue I have is that the code executes and copies the file when using
ImpersonateLoggedOnUser, but not when using a WindowsImpersonationContext.
This wouldn't be an issue except that the ImpersonateLoggedOnUser does not
work when called on Windows 2000/2003 Pro/Server, only Windows XP.

The question would be why is this so and what can I do to get it working
with managed code. Note that the logon type and provider listed here are
only
test values that I'm using, but no combination yields any better result
other
than error 1326 "Logon failure: unknown user name or bad password".

Thanks.


using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;

namespace SecurityTest
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class SecurityTest
{
[DllImport("advapi32.DLL", SetLastError = true)]
public static extern int LogonUser(string lpszUsername, string lpszDomain,
string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr
phToken);
[DllImport("advapi32.DLL")]
public static extern bool ImpersonateLoggedOnUser(IntPtr hToken); //
handle to token for logged-on user
[DllImport("advapi32.DLL")]
public static extern bool RevertToSelf();

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
IntPtr admin_token;

//WindowsIdentity wid_current = WindowsIdentity.GetCurrent();

WindowsIdentity wid_admin = null;
WindowsImpersonationContext wic = null;

try
{
Console.WriteLine("Copying file...");
if (LogonUser("Administrator", "192.168.0.1", "password", 9, 0, out
admin_token) != 0)
{
//ImpersonateLoggedOnUser(admin_token);
wid_admin = new WindowsIdentity(admin_token);
wic = wid_admin.Impersonate();
System.IO.File.Copy("C:\\rpmtest.txt",
"\\\\192.168.0.1\\bb.uploads\\test.txt", true);
Console.WriteLine("Copy succeeded");
}
else Console.WriteLine("Copy Failed");
}
catch (System.Exception se)
{
int ret = Marshal.GetLastWin32Error();
Console.WriteLine(ret.ToString(), "Error code: " + ret.ToString());
Console.WriteLine(se.Message);
}
finally
{
//RevertToSelf();
if (wic != null) wic.Undo();
}

//Console.ReadLine();
}
}
}
 
W

Willy Denoyette [MVP]

BLiTZWiNG said:
Ok, I remember you said this from your last post, and I've been trying to
get
it to work from there. Winbase.h defines LOGON32_PROVIDER_WINNT50 as 3. If
I
put 5 in I get error 87 (invalid parameter). 3 gives me the same error as
0
(error 1326) when using managed code, and no error (and successful copy)
when
using unmanaged code (but only on XP). I get the same errors using 8 and 3
in
managed code also (LOGON32_LOGON_NETWORK_CLEARTEXT and
LOGON32_LOGON_NETWORK).

This is the bit that has me confused. For all intents and purposes it
appears to work in XP but only with unmanaged code.


Sorry, 5 was a typo,
if(LogonUser(userName, domain, password, 9, 3, ref token) != 0)
was correct ;-).
Some questions:
error 1326 is returned after what call, LogonUser or ? (I don't see why
LogonUser would fail depending the Impersonation method used later on).
What exactly do you mean with managed/unmanaged code in this context?
Is it:
ImpersonateLoggedOnUser = unmanaged?
Impersonate = managed?
What system (OS) are you connceting to, is it the same in all cases?


Note : you should add SetLastError = true)] to your ImpersonateLoggedOnUser
declaration too.
9 is the only logon option you can use here, the logon access token
should only be used to access the remote resource, the local file must still
be read with the original user's access token.

Willy.
 
G

Guest

To answer your questions:

I refer to the DLL import functions as unmanaged, though this is probably
incorrect to do so. So LogonUser, ImpersonateLoggedOnUser and RevertToSelf I
refer to as unmanaged, and the .NET classes as managed.

The OS I am connecting to is Windows 2003 Server in all cases. I assume the
server is setup in a valid manner as I can copy files to it from my machine
but only if I use unmanaged (I should start referring to it as imported) code.

Note that I have since modified my code in the posted example to better
handle the errors returned.

Ok, structured test results:

When calling LogonUser with LOGON32_LOGON_NETWORK (3) or
LOGON32_LOGON_NETWORK_CLEARTEXT (8) and either 0 or 3 as the provider, the
code fails at LogonUser with GetLastWin32Error being 1326 (Logon failure).

When using a WindowsIdentity class and calling LogonUser with
LOGON32_LOGON_NEW_CREDENTIALS (9) and either 0 or 3 as the provider, the code
fails on WindowsIdentity.Impersonate(). The error code from GetLastWin32Error
is 0, and the exception message is "Unable to impersonate user" on Windows
XP. On Windows 2000 Professional the code fails at LogonUser with error code
1314 "A required privilege is not held by the client."

When using DLL imported code ImpersonateLoggedOnUser and calling LogonUser
with LOGON32_LOGON_NEW_CREDENTIALS (9) and either 0 or 3 as the provider, the
code succeeds at copying the file on Windows XP. On Windows 2000 Professional
the code fails at LogonUser with error code 1314 "A required privilege is not
held by the client."

This is the latest set of results, which now seem to be consistent as this
whole testing process has had to suffer my learning curve. I was previously
getting different errors on the 2k pro box, but it seems now that I know I
need to use (9) that I can consistently get 1314 at LogonUser, which if I
could fix would solve my immediate issue of needing to get the code working.
However I still don't understand why WindowsIdentity.Impersonate() always
fails.

Thanks.

Willy Denoyette said:
BLiTZWiNG said:
Ok, I remember you said this from your last post, and I've been trying to
get
it to work from there. Winbase.h defines LOGON32_PROVIDER_WINNT50 as 3. If
I
put 5 in I get error 87 (invalid parameter). 3 gives me the same error as
0
(error 1326) when using managed code, and no error (and successful copy)
when
using unmanaged code (but only on XP). I get the same errors using 8 and 3
in
managed code also (LOGON32_LOGON_NETWORK_CLEARTEXT and
LOGON32_LOGON_NETWORK).

This is the bit that has me confused. For all intents and purposes it
appears to work in XP but only with unmanaged code.


Sorry, 5 was a typo,
if(LogonUser(userName, domain, password, 9, 3, ref token) != 0)
was correct ;-).
Some questions:
error 1326 is returned after what call, LogonUser or ? (I don't see why
LogonUser would fail depending the Impersonation method used later on).
What exactly do you mean with managed/unmanaged code in this context?
Is it:
ImpersonateLoggedOnUser = unmanaged?
Impersonate = managed?
What system (OS) are you connceting to, is it the same in all cases?


Note : you should add SetLastError = true)] to your ImpersonateLoggedOnUser
declaration too.
9 is the only logon option you can use here, the logon access token
should only be used to access the remote resource, the local file must still
be read with the original user's access token.

Willy.
 
M

Manfred Braun

Hi All,

and sorry, that I break in here ......

I have also to connect to remote files and process them, but I do not
understand the InterOp code completely. What I do, from a thread from the
threadpool is just simply:

comp = new DirectoryEntry
(
String.Format("{0}://{1},computer", adsDefaultProvider,
args.computerName),
args.userName,
args.password,
AuthenticationTypes.Secure
);
comp.UsePropertyCache = false;
string os = comp.Properties["OperatingSystemVersion"][0].ToString();
[I do not really need the "os", but the trsuted channel]

After I - so I call it - "establish a secure channel" this way, I can access
the files also, no interop/win32 code neccessary.

I verified this to work from w2l to w2k and to xp. I needed a long time to
trust this snippet of code [ok, I do it not for 100%, but it works].

Any thoughts and objections are really very welcome. I came to this idea,
because I do not really understand impersonation. How can I impersonate a
thread on the local machine with credentials, which are not valid on the
local machine, but on the remote one!??

Thanks so far and best regards,

Best regards,
Manfred Braun

(Private)
Mannheim
Germany

mailto:[email protected]
(Remove the anti-spam-underscore to mail me!)



BLiTZWiNG said:
To answer your questions:

I refer to the DLL import functions as unmanaged, though this is probably
incorrect to do so. So LogonUser, ImpersonateLoggedOnUser and RevertToSelf I
refer to as unmanaged, and the .NET classes as managed.

The OS I am connecting to is Windows 2003 Server in all cases. I assume the
server is setup in a valid manner as I can copy files to it from my machine
but only if I use unmanaged (I should start referring to it as imported) code.

Note that I have since modified my code in the posted example to better
handle the errors returned.

Ok, structured test results:

When calling LogonUser with LOGON32_LOGON_NETWORK (3) or
LOGON32_LOGON_NETWORK_CLEARTEXT (8) and either 0 or 3 as the provider, the
code fails at LogonUser with GetLastWin32Error being 1326 (Logon failure).

When using a WindowsIdentity class and calling LogonUser with
LOGON32_LOGON_NEW_CREDENTIALS (9) and either 0 or 3 as the provider, the code
fails on WindowsIdentity.Impersonate(). The error code from GetLastWin32Error
is 0, and the exception message is "Unable to impersonate user" on Windows
XP. On Windows 2000 Professional the code fails at LogonUser with error code
1314 "A required privilege is not held by the client."

When using DLL imported code ImpersonateLoggedOnUser and calling LogonUser
with LOGON32_LOGON_NEW_CREDENTIALS (9) and either 0 or 3 as the provider, the
code succeeds at copying the file on Windows XP. On Windows 2000 Professional
the code fails at LogonUser with error code 1314 "A required privilege is not
held by the client."

This is the latest set of results, which now seem to be consistent as this
whole testing process has had to suffer my learning curve. I was previously
getting different errors on the 2k pro box, but it seems now that I know I
need to use (9) that I can consistently get 1314 at LogonUser, which if I
could fix would solve my immediate issue of needing to get the code working.
However I still don't understand why WindowsIdentity.Impersonate() always
fails.

Thanks.

Willy Denoyette said:
BLiTZWiNG said:
Ok, I remember you said this from your last post, and I've been trying to
get
it to work from there. Winbase.h defines LOGON32_PROVIDER_WINNT50 as 3. If
I
put 5 in I get error 87 (invalid parameter). 3 gives me the same error as
0
(error 1326) when using managed code, and no error (and successful copy)
when
using unmanaged code (but only on XP). I get the same errors using 8 and 3
in
managed code also (LOGON32_LOGON_NETWORK_CLEARTEXT and
LOGON32_LOGON_NETWORK).

This is the bit that has me confused. For all intents and purposes it
appears to work in XP but only with unmanaged code.


Sorry, 5 was a typo,
if(LogonUser(userName, domain, password, 9, 3, ref token) != 0)
was correct ;-).
Some questions:
error 1326 is returned after what call, LogonUser or ? (I don't see why
LogonUser would fail depending the Impersonation method used later on).
What exactly do you mean with managed/unmanaged code in this context?
Is it:
ImpersonateLoggedOnUser = unmanaged?
Impersonate = managed?
What system (OS) are you connceting to, is it the same in all cases?


Note : you should add SetLastError = true)] to your ImpersonateLoggedOnUser
declaration too.
9 is the only logon option you can use here, the logon access token
should only be used to access the remote resource, the local file must still
be read with the original user's access token.

Willy.
 
W

Willy Denoyette [MVP]

See inline ***

Willy.

BLiTZWiNG said:
To answer your questions:

I refer to the DLL import functions as unmanaged, though this is probably
incorrect to do so. So LogonUser, ImpersonateLoggedOnUser and RevertToSelf
I
refer to as unmanaged, and the .NET classes as managed.
*** No problem, just to be sure we are talking about the same thing.
The OS I am connecting to is Windows 2003 Server in all cases. I assume
the
server is setup in a valid manner as I can copy files to it from my
machine
but only if I use unmanaged (I should start referring to it as imported)
code.

Note that I have since modified my code in the posted example to better
handle the errors returned.

Ok, structured test results:

When calling LogonUser with LOGON32_LOGON_NETWORK (3) or
LOGON32_LOGON_NETWORK_CLEARTEXT (8) and either 0 or 3 as the provider, the
code fails at LogonUser with GetLastWin32Error being 1326 (Logon failure).
When using a WindowsIdentity class and calling LogonUser with
LOGON32_LOGON_NEW_CREDENTIALS (9) and either 0 or 3 as the provider, the
code
fails on WindowsIdentity.Impersonate(). The error code from
GetLastWin32Error
is 0, and the exception message is "Unable to impersonate user" on Windows
XP. On Windows 2000 Professional the code fails at LogonUser with error
code
1314 "A required privilege is not held by the client."

***
1. Not sure why WindowsIdentity.Impersonate(). works for me on XP SP2
running .NET v1.1 SP1, will try to investigate why it throws on you. Note
that LogonUser didn't fail, but aparantly Impersonate doesn't like the token
returned.
2. Windows 2000 needs the "Act as part of the operating system" privilege
(also called TCB privilege) in order to call LogonUser, this super privilege
is no longer required on XP and W2K3. This is very unfortunate, but unless
you are willing to spend some hard time to interop with the SSP API's there
is no other way to call LogonUser than enabling the TCB privilege for the
calling user account..

When using DLL imported code ImpersonateLoggedOnUser and calling LogonUser
with LOGON32_LOGON_NEW_CREDENTIALS (9) and either 0 or 3 as the provider,
the
code succeeds at copying the file on Windows XP. On Windows 2000
Professional
the code fails at LogonUser with error code 1314 "A required privilege is
not
held by the client."

*** See above for privilege error on W2K.
This is the latest set of results, which now seem to be consistent as this
whole testing process has had to suffer my learning curve. I was
previously
getting different errors on the 2k pro box, but it seems now that I know I
need to use (9) that I can consistently get 1314 at LogonUser, which if I
could fix would solve my immediate issue of needing to get the code
working.
However I still don't understand why WindowsIdentity.Impersonate() always
fails.

*** To resume:
- Call LogonUser with Logontype 9 and preferaby specifying the Kerberos
authentication provider (3).
- Add user account to the TCB when running your code on W2K :-(
 
W

Willy Denoyette [MVP]

Manfred,

See inline.

Willy.

Manfred Braun said:
Hi All,

and sorry, that I break in here ......

I have also to connect to remote files and process them, but I do not
understand the InterOp code completely. What I do, from a thread from the
threadpool is just simply:

comp = new DirectoryEntry
(
String.Format("{0}://{1},computer", adsDefaultProvider,
args.computerName),
args.userName,
args.password,
AuthenticationTypes.Secure
);
comp.UsePropertyCache = false;
string os = comp.Properties["OperatingSystemVersion"][0].ToString();
[I do not really need the "os", but the trsuted channel]

After I - so I call it - "establish a secure channel" this way, I can
access
the files also, no interop/win32 code neccessary.

*** {0} contains WinNT, right? and {1} is the remote system name.
By doing this you are currently establishing a "network logon session" for
the current user (local logon session) , using the credentials arg.userName
and arg.password to access the remote resource. The network session is
shared by all applications running in the same user session.
The result of this is the same as you would create a remote session using
the "net use ..." command.
I verified this to work from w2l to w2k and to xp. I needed a long time to
trust this snippet of code [ok, I do it not for 100%, but it works].
*** While this might work for you it is an indication that your network
resources are not secured, you are establishing a network logon to access
"Directory services", but you can use the same session credentials to access
file server resources and guess what else :).
 
G

Guest

Thanks Willy, you saved the day =) It took some fiddling but I got it to work
with the TCB priv. Thanks so much for your help here.

I'm also using XP SP2 with .NET v1.1.. but I'm not sure about SP1 ... That
might be my problem, I'll have to check and see if I have SP1 installed.
 
P

pdepmcp

the problem is that you have to set the SE_TCB_NAME privilege to the
user before running the code if running on win2k (you don't need to do
so on win xp).
To do so open secpol.msc and select local policies \ user right
assignments and add "act as part of the system" to the user that will
execute your code.

remember that if you use LOGON32_LOGON_NEW_CREDENTIALS
(9) you HAVE TO use LOGON32_PROVIDER_WINNT50 (3).

after doing so I suggest you to logout and login again.
this should do the magic. if it doesn't try adding something like
ManagementObject mo = new ManagementObject(new ManagementPath(
));mo.Scope.Options.EnablePrivileges = true;

to your code BEFORE calling LogonUser. i am not very insiede c# so i
can't help you more with this.

regards
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top