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.