LogonUserA / Impersonation

  • Thread starter Thread starter Craig S
  • Start date Start date
C

Craig S

I've implemented the impersonation method shown here:
http://support.microsoft.com/?id=306158 under the section "Impersonate a
Specific User in Code". Essentially just interop the LogonUserA function in
advapi32.dll, execute the code you want, and then undo impersonation.

However, this only works for the domain that the machine you're running from
is joined to. I want to run this code on a web server just in a workgroup
and authenticate the user on some other domain (which it obviously has
network access to) for purposes of retrieving files from systems on that
domain.

Is it possible using this method? Any other suggestions how to accomplish
this? All systems are various flavors of Win2k3, but for compatibility with
Citrix I'm stuck in Framework 1.1 for right now.

Thanks in advance,
Craig

PS - Bonus/Side question - Is this easier in .Net 2.0?
 
Hello Craig,
However, this only works for the domain that the machine you're
running from is joined to. I want to run this code on a web server
just in a workgroup and authenticate the user on some other domain
(which it obviously has network access to) for purposes of retrieving
files from systems on that domain.

I'm almost 100% sure this method won't work. For LogonUser to work correctly
you must be joined to the domain that you are attempting to logon to. Otherwise
you could sit there and do dictionary attacks against domains from your house
:)
Is it possible using this method? Any other suggestions how to
accomplish this? All systems are various flavors of Win2k3, but for
compatibility with Citrix I'm stuck in Framework 1.1 for right now.

One thing you could try is adding your logon information to your network
credentials cache. I have no idea if this will work or not but it's the
only thing that's coming to mind right now.
 
....am I going about this wrong? Maybe a c# example of an SMB client that
allows authentication? I'm googling for one now...
 
| Hello Craig,
|
| > However, this only works for the domain that the machine you're
| > running from is joined to. I want to run this code on a web server
| > just in a workgroup and authenticate the user on some other domain
| > (which it obviously has network access to) for purposes of retrieving
| > files from systems on that domain.
|
| I'm almost 100% sure this method won't work. For LogonUser to work
correctly
| you must be joined to the domain that you are attempting to logon to.
Otherwise
| you could sit there and do dictionary attacks against domains from your
house
| :)
|
| > Is it possible using this method? Any other suggestions how to
| > accomplish this? All systems are various flavors of Win2k3, but for
| > compatibility with Citrix I'm stuck in Framework 1.1 for right now.
|
| One thing you could try is adding your logon information to your network
| credentials cache. I have no idea if this will work or not but it's the
| only thing that's coming to mind right now.
|
| --
| Jared Parsons [MSFT]
| (e-mail address removed)
| http://jaredparsons.blogspot.com
|
|

Jared,

You can call LogonUser from a non domain member, specifying any domain you
like, as long as the credentials passed as arguments can be authenticated on
that domain you supply as argument, LogonUser will succeed and return a
valid access token. How would you ever call LogonUser from Win98 for
instance?


Willy.
 
Hello Craig,
...am I going about this wrong? Maybe a c# example of an SMB client
that allows authentication? I'm googling for one now...

For the instances where you are in the same domain you are taking the correct
approach. Once you try to mix domain and not domain you are going to run
into some issues.
 
Hello Willy Denoyette [MVP],
You can call LogonUser from a non domain member, specifying any domain
you like, as long as the credentials passed as arguments can be
authenticated on that domain you supply as argument, LogonUser will
succeed and return a valid access token. How would you ever call
LogonUser from Win98 for instance?

Good thing I added "almost" :).

Craig must be running into some other issue. What error code are you getting
back when attempt the LogonUser call Craig?

--
Jared Parsons [MSFT]
(e-mail address removed)
http://jaredparsons.blogspot.com
message
| Hello Craig,
|
| > However, this only works for the domain that the machine you're
| > running from is joined to. I want to run this code on a web
server
| > just in a workgroup and authenticate the user on some other domain
| > (which it obviously has network access to) for purposes of
retrieving
| > files from systems on that domain.
|
| I'm almost 100% sure this method won't work. For LogonUser to work
correctly
| you must be joined to the domain that you are attempting to logon
to.
Otherwise
| you could sit there and do dictionary attacks against domains from
your
house
| :)
|
| > Is it possible using this method? Any other suggestions how to
| > accomplish this? All systems are various flavors of Win2k3, but
for
| > compatibility with Citrix I'm stuck in Framework 1.1 for right
now.
|
| One thing you could try is adding your logon information to your
network
| credentials cache. I have no idea if this will work or not but it's
the
| only thing that's coming to mind right now.
|
| --
| Jared Parsons [MSFT]
| (e-mail address removed)
| http://jaredparsons.blogspot.com
|
|
Jared,

You can call LogonUser from a non domain member, specifying any domain
you like, as long as the credentials passed as arguments can be
authenticated on that domain you supply as argument, LogonUser will
succeed and return a valid access token. How would you ever call
LogonUser from Win98 for instance?

Willy.
 
So we're talking about the same value, I'm assuming you mean a integer
return value from LogonUserA().

A successful attempt where I authenticate against a domain the system is
currently a member of returns 0 (as expected.)

An attempt where I try to authenticate using valid credentials but against a
domain the system executing the code is not a member of returns a 1.

Both return very quickly (under 250ms) so there's no issues with a "timeout"
or anything, it's just flatly rejected.

And just FYI, both domains are on the same local network (no firewalls, WAN,
etc) but NOT members of the same forest or in any other way related to each
other. I've tried it from a workstation in another domain as well as a
simple web server that's just a member of a workgroup. Both function
identically.

Craig


Jared Parsons said:
Hello Willy Denoyette [MVP],
You can call LogonUser from a non domain member, specifying any domain
you like, as long as the credentials passed as arguments can be
authenticated on that domain you supply as argument, LogonUser will
succeed and return a valid access token. How would you ever call
LogonUser from Win98 for instance?

Good thing I added "almost" :).
Craig must be running into some other issue. What error code are you
getting back when attempt the LogonUser call Craig?
--
Jared Parsons [MSFT]
(e-mail address removed)
http://jaredparsons.blogspot.com
message
| Hello Craig,
|
| > However, this only works for the domain that the machine you're
| > running from is joined to. I want to run this code on a web
server
| > just in a workgroup and authenticate the user on some other domain
| > (which it obviously has network access to) for purposes of
retrieving
| > files from systems on that domain.
|
| I'm almost 100% sure this method won't work. For LogonUser to work
correctly
| you must be joined to the domain that you are attempting to logon
to.
Otherwise
| you could sit there and do dictionary attacks against domains from
your
house
| :)
|
| > Is it possible using this method? Any other suggestions how to
| > accomplish this? All systems are various flavors of Win2k3, but
for
| > compatibility with Citrix I'm stuck in Framework 1.1 for right
now.
|
| One thing you could try is adding your logon information to your
network
| credentials cache. I have no idea if this will work or not but it's
the
| only thing that's coming to mind right now.
|
| --
| Jared Parsons [MSFT]
| (e-mail address removed)
| http://jaredparsons.blogspot.com
|
|
Jared,

You can call LogonUser from a non domain member, specifying any domain
you like, as long as the credentials passed as arguments can be
authenticated on that domain you supply as argument, LogonUser will
succeed and return a valid access token. How would you ever call
LogonUser from Win98 for instance?

Willy.
 
Hello Craig,
So we're talking about the same value, I'm assuming you mean a integer
return value from LogonUserA().

After the call fails, call GetLastError() and tell me what that says. Make
sure to add SetLastError=true to the DllImport attribute on LogonUserA.
And just FYI, both domains are on the same local network (no
firewalls, WAN, etc) but NOT members of the same forest or in any
other way related to each other. I've tried it from a workstation in
another domain as well as a simple web server that's just a member of
a workgroup. Both function identically.

What is the second domain? I thought there was the domain you are trying
to log onto and the workgroup where it's failing?

Knowing that there are multiple domains here, are you specifing the full
domain name (foo.bar.com) in the credentials or the short version?

--
Jared Parsons [MSFT]
(e-mail address removed)
http://jaredparsons.blogspot.com

A successful attempt where I authenticate against a domain the system
is currently a member of returns 0 (as expected.)

An attempt where I try to authenticate using valid credentials but
against a domain the system executing the code is not a member of
returns a 1.

Both return very quickly (under 250ms) so there's no issues with a
"timeout" or anything, it's just flatly rejected.


Craig

Hello Willy Denoyette [MVP],
You can call LogonUser from a non domain member, specifying any
domain you like, as long as the credentials passed as arguments can
be authenticated on that domain you supply as argument, LogonUser
will succeed and return a valid access token. How would you ever
call LogonUser from Win98 for instance?
Good thing I added "almost" :).
Craig must be running into some other issue. What error code are you
getting back when attempt the LogonUser call Craig?
--
Jared Parsons [MSFT]
(e-mail address removed)
http://jaredparsons.blogspot.com
message
| Hello Craig,
|
| > However, this only works for the domain that the machine you're
| > running from is joined to. I want to run this code on a web
server
| > just in a workgroup and authenticate the user on some other
domain
| > (which it obviously has network access to) for purposes of
retrieving
| > files from systems on that domain.
|
| I'm almost 100% sure this method won't work. For LogonUser to
work
correctly
| you must be joined to the domain that you are attempting to logon
to.
Otherwise
| you could sit there and do dictionary attacks against domains from
your
house
| :)
|
| > Is it possible using this method? Any other suggestions how to
| > accomplish this? All systems are various flavors of Win2k3, but
for
| > compatibility with Citrix I'm stuck in Framework 1.1 for right
now.
|
| One thing you could try is adding your logon information to your
network
| credentials cache. I have no idea if this will work or not but
it's
the
| only thing that's coming to mind right now.
|
| --
| Jared Parsons [MSFT]
| (e-mail address removed)
| http://jaredparsons.blogspot.com
|
|
Jared,
You can call LogonUser from a non domain member, specifying any
domain you like, as long as the credentials passed as arguments can
be authenticated on that domain you supply as argument, LogonUser
will succeed and return a valid access token. How would you ever
call LogonUser from Win98 for instance?

Willy.
 
Duh, sorry. Marshal.GetLastWin32Error() is 1326. I did some quick research
and it's essentially the generic and most common error meaning "Invalid
username or password", but I've tried several now that I know to be valid.
I'm continuing to google that error but it's not helping so far.

Regarding your question about how I'm addressing the domains, I've actually
tried both and the short and fully qualified. Both always work for the
domain the system belongs to but neither work for the domain to which it
does not belong. I've tried the scenario both ways, from a machine on a
different domain and a machine only on a workgroup (with of course a local
domain). In my estimation the net result should be the same, and it has
been thus far. I can only get it to authenticate to a domain in which it is
a member. Is it perhaps an "ACL like" setting in AD preventing me from
querying it on the server side?

I'm going to start looking at security logs to see if it's actually trying
to authenticate me on the local domain even though I specify a different
domain name? That's the only thing I can think of to do - your thoughts?

Craig

Just so you have the code, here's what I'm doing. The only code changed was
the line with name/pass/domain.

using System;

using System.Collections;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Web;

using System.Web.SessionState;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.HtmlControls;

using System.Web.Security;

using System.Security.Principal;

using System.Runtime.InteropServices;

namespace TestImpersonation

{

/// <summary>

/// Summary description for WebForm1.

/// </summary>

public class Default : System.Web.UI.Page

{

private void Page_Load(object sender, System.EventArgs e)

{

int resultCode = int.MinValue;

if(impersonateValidUser("username", "domain.com", "password",out
resultCode))

{

Response.Write("Worked! resultCode: " +resultCode.ToString());

undoImpersonation();

}

else

{

Response.Write("Didn't Work. resultCode: " +resultCode.ToString() +"
getLastError: " +Marshal.GetLastWin32Error().ToString());

}

}

#region Web Form Designer generated code

override protected void OnInit(EventArgs e)

{

//

// CODEGEN: This call is required by the ASP.NET Web Form Designer.

//

InitializeComponent();

base.OnInit(e);

}


/// <summary>

/// Required method for Designer support - do not modify

/// the contents of this method with the code editor.

/// </summary>

private void InitializeComponent()

{

this.Load += new System.EventHandler(this.Page_Load);

}

#endregion

public const int LOGON32_LOGON_INTERACTIVE = 2;

public const int LOGON32_PROVIDER_DEFAULT = 0;

WindowsImpersonationContext impersonationContext;

[DllImport("advapi32.dll", SetLastError=true)]

public static extern int LogonUserA(String lpszUserName,

String lpszDomain,

String lpszPassword,

int dwLogonType,

int dwLogonProvider,

ref IntPtr phToken);

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

public static extern int DuplicateToken(IntPtr hToken,

int impersonationLevel,

ref IntPtr hNewToken);

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

public static extern bool RevertToSelf();

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]

public static extern bool CloseHandle(IntPtr handle);

[DllImport("kernel32.dll", EntryPoint="GetLastError", SetLastError=false,

ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]

public static extern Int32 GetLastError();

private bool impersonateValidUser(String userName, String domain, String
password, out int resultCode)

{

resultCode = int.MinValue;

WindowsIdentity tempWindowsIdentity;

IntPtr token = IntPtr.Zero;

IntPtr tokenDuplicate = IntPtr.Zero;

if(RevertToSelf())

{

resultCode = LogonUserA(userName, domain, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token);

if (resultCode != 0)

{

if(DuplicateToken(token, 2, ref tokenDuplicate) != 0)

{

tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);

impersonationContext = tempWindowsIdentity.Impersonate();

if (impersonationContext != null)

{

CloseHandle(token);

CloseHandle(tokenDuplicate);

return true;

}

}

}

}

if(token!= IntPtr.Zero)

CloseHandle(token);

if(tokenDuplicate!=IntPtr.Zero)

CloseHandle(tokenDuplicate);

return false;

}

private void undoImpersonation()

{

impersonationContext.Undo();

}

}

}



Jared Parsons said:
Hello Craig,
So we're talking about the same value, I'm assuming you mean a integer
return value from LogonUserA().

After the call fails, call GetLastError() and tell me what that says.
Make sure to add SetLastError=true to the DllImport attribute on
LogonUserA.
And just FYI, both domains are on the same local network (no
firewalls, WAN, etc) but NOT members of the same forest or in any
other way related to each other. I've tried it from a workstation in
another domain as well as a simple web server that's just a member of
a workgroup. Both function identically.

What is the second domain? I thought there was the domain you are trying
to log onto and the workgroup where it's failing?
Knowing that there are multiple domains here, are you specifing the full
domain name (foo.bar.com) in the credentials or the short version?
--
Jared Parsons [MSFT]
(e-mail address removed)
http://jaredparsons.blogspot.com

A successful attempt where I authenticate against a domain the system
is currently a member of returns 0 (as expected.)

An attempt where I try to authenticate using valid credentials but
against a domain the system executing the code is not a member of
returns a 1.

Both return very quickly (under 250ms) so there's no issues with a
"timeout" or anything, it's just flatly rejected.


Craig

Hello Willy Denoyette [MVP],

You can call LogonUser from a non domain member, specifying any
domain you like, as long as the credentials passed as arguments can
be authenticated on that domain you supply as argument, LogonUser
will succeed and return a valid access token. How would you ever
call LogonUser from Win98 for instance?

Good thing I added "almost" :).
Craig must be running into some other issue. What error code are you
getting back when attempt the LogonUser call Craig?
--
Jared Parsons [MSFT]
(e-mail address removed)
http://jaredparsons.blogspot.com
message
| Hello Craig,
|
| > However, this only works for the domain that the machine you're
| > running from is joined to. I want to run this code on a web
server
| > just in a workgroup and authenticate the user on some other
domain
| > (which it obviously has network access to) for purposes of
retrieving
| > files from systems on that domain.
|
| I'm almost 100% sure this method won't work. For LogonUser to
work
correctly
| you must be joined to the domain that you are attempting to logon
to.
Otherwise
| you could sit there and do dictionary attacks against domains from
your
house
| :)
|
| > Is it possible using this method? Any other suggestions how to
| > accomplish this? All systems are various flavors of Win2k3, but
for
| > compatibility with Citrix I'm stuck in Framework 1.1 for right
now.
|
| One thing you could try is adding your logon information to your
network
| credentials cache. I have no idea if this will work or not but
it's
the
| only thing that's coming to mind right now.
|
| --
| Jared Parsons [MSFT]
| (e-mail address removed)
| http://jaredparsons.blogspot.com
|
|
Jared,
You can call LogonUser from a non domain member, specifying any
domain you like, as long as the credentials passed as arguments can
be authenticated on that domain you supply as argument, LogonUser
will succeed and return a valid access token. How would you ever
call LogonUser from Win98 for instance?

Willy.
 
Just to save some time if you go to look up that error code, it's here:
http://msdn.microsoft.com/library/d...debug/base/system_error_codes__1300-1699_.asp

ERROR_LOGON_FAILURE
1326 Logon failure: unknown user name or bad password.



Craig S said:
Duh, sorry. Marshal.GetLastWin32Error() is 1326. I did some quick
research and it's essentially the generic and most common error meaning
"Invalid username or password", but I've tried several now that I know to
be valid. I'm continuing to google that error but it's not helping so far.

Regarding your question about how I'm addressing the domains, I've
actually tried both and the short and fully qualified. Both always work
for the domain the system belongs to but neither work for the domain to
which it does not belong. I've tried the scenario both ways, from a
machine on a different domain and a machine only on a workgroup (with of
course a local domain). In my estimation the net result should be the
same, and it has been thus far. I can only get it to authenticate to a
domain in which it is a member. Is it perhaps an "ACL like" setting in AD
preventing me from querying it on the server side?

I'm going to start looking at security logs to see if it's actually trying
to authenticate me on the local domain even though I specify a different
domain name? That's the only thing I can think of to do - your thoughts?

Craig


(Snip rest of thread)
 
Hello Willy Denoyette [MVP],
How would you ever call LogonUser from Win98 for instance?

You can't. LogonUser isn't implemented until Windows NT. Before that you
had to go through the SSPI interfaces

--
Jared Parsons [MSFT]
(e-mail address removed)
http://jaredparsons.blogspot.com
message
| Hello Craig,
|
| > However, this only works for the domain that the machine you're
| > running from is joined to. I want to run this code on a web
server
| > just in a workgroup and authenticate the user on some other domain
| > (which it obviously has network access to) for purposes of
retrieving
| > files from systems on that domain.
|
| I'm almost 100% sure this method won't work. For LogonUser to work
correctly
| you must be joined to the domain that you are attempting to logon
to.
Otherwise
| you could sit there and do dictionary attacks against domains from
your
house
| :)
|
| > Is it possible using this method? Any other suggestions how to
| > accomplish this? All systems are various flavors of Win2k3, but
for
| > compatibility with Citrix I'm stuck in Framework 1.1 for right
now.
|
| One thing you could try is adding your logon information to your
network
| credentials cache. I have no idea if this will work or not but it's
the
| only thing that's coming to mind right now.
|
| --
| Jared Parsons [MSFT]
| (e-mail address removed)
| http://jaredparsons.blogspot.com
|
|
Jared,

You can call LogonUser from a non domain member, specifying any domain
you like, as long as the credentials passed as arguments can be
authenticated on that domain you supply as argument, LogonUser will
succeed and return a valid access token. How would you ever call
LogonUser from Win98 for instance?

Willy.
 
Hello Craig,
I'm going to start looking at security logs to see if it's actually
trying to authenticate me on the local domain even though I specify a
different domain name? That's the only thing I can think of to do -
your thoughts?

The only thing I'm left with is my original assertion that you cannot call
LogonUser() from a non-domain machine. After further thought it just doesn't
seem logically possible since LogonUser returns a Token. What would happen
if I then passed that token into CreateProcessWithToken() or ImpersonateLoggedOnUser()?
That should not work because what relevance does my domain based token (that
the current machine is not a part of) mean to this machine? Nothing.

Willy if you have feedback here please post it. I play with this API from
time to time but I'm certainly not an expert here.

--
Jared Parsons [MSFT]
(e-mail address removed)
http://jaredparsons.blogspot.com
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of included script samples are subject to the terms specified at http://www.microsoft.com/info/cpyright.htm
 
Hi Jared,

That's true, my bad, Win9X uses SSPI to get an access token for remote
resource access, these OS don't have a LSA that can handle the LsaLogonUser
API calls (called by LogonUser) to redirect the request to another server's
(or DC) LSA.
But apart from that, being a domain member is not a requirement imposed by
LogonUser.

Willy.

| Hello Willy Denoyette [MVP],
|
| > How would you ever call LogonUser from Win98 for instance?
|
| You can't. LogonUser isn't implemented until Windows NT. Before that you
| had to go through the SSPI interfaces
|
| --
| Jared Parsons [MSFT]
| (e-mail address removed)
| http://jaredparsons.blogspot.com
|
| > message
| > | > | Hello Craig,
| > |
| > | > However, this only works for the domain that the machine you're
| > | > running from is joined to. I want to run this code on a web
| > server
| > | > just in a workgroup and authenticate the user on some other domain
| > | > (which it obviously has network access to) for purposes of
| > retrieving
| > | > files from systems on that domain.
| > |
| > | I'm almost 100% sure this method won't work. For LogonUser to work
| > correctly
| > | you must be joined to the domain that you are attempting to logon
| > to.
| > Otherwise
| > | you could sit there and do dictionary attacks against domains from
| > your
| > house
| > | :)
| > |
| > | > Is it possible using this method? Any other suggestions how to
| > | > accomplish this? All systems are various flavors of Win2k3, but
| > for
| > | > compatibility with Citrix I'm stuck in Framework 1.1 for right
| > now.
| > |
| > | One thing you could try is adding your logon information to your
| > network
| > | credentials cache. I have no idea if this will work or not but it's
| > the
| > | only thing that's coming to mind right now.
| > |
| > | --
| > | Jared Parsons [MSFT]
| > | (e-mail address removed)
| > | http://jaredparsons.blogspot.com
| > |
| > |
| > Jared,
| >
| > You can call LogonUser from a non domain member, specifying any domain
| > you like, as long as the credentials passed as arguments can be
| > authenticated on that domain you supply as argument, LogonUser will
| > succeed and return a valid access token. How would you ever call
| > LogonUser from Win98 for instance?
| >
| > Willy.
| >
|
|
 
Back
Top