Wednesday, March 5, 2008

Work Around When Impersonating, Authenticating Across Non Trusted Domains

The Problem

The other day I was taxed with impersonating a credential that was in another domain! Let me explain. I have two machines, one in DomainA and the other in DomainB. We have code that needs to run on DomainA\Machine1 and copy files to a share on DomainB\Machine2.

It's of note that because there's no trust between DomainA and DomainB there is no credential that exists that we can use to copy these files that both domains will know about.

For example, you can set up a share on DomainB\Machine2, but who's credentials do you give rights to? Even if you give rights to 'Everybody' that still just means everybody in the domain (DomainB) and on the local machine, no one in DomainA will be able to get access to that share. Further more you can't run code on DomainA\Machine1 and try to impersonate a credential in DomainB, you'll get the following exception.

System.ComponentModel.Win32Exception: Logon failure: unknown user name or bad password.

This makes sense, DomainA has no idea what the credentials are like in DomainB and has no real means of checking.

The Workaround

I remember reading an article about Remote Debugging not too long ago and a similar problem I faced at a client location; trying to remotely debug an machine in a foreign domain when you don't have a domain account! It's complicated because the machine running the remote debugger needs to know that you're a safe user before it allows you to block a whole w3p.exe process. As it turns out you can create a credential with the same name and password on both computers and they'll honor each others credentials and their associated rights. The table below is from said article. It describes what accounts you can use to debug remotely given two different machines. Notice that the 'Local accounts with same user name and password on both computers' column is Yes ever time. We can apply this same technique to solve our problem. We create two local accounts with the same username and password on both machines and we're in business.

Computer setup Local System account Domain account Local accounts with the same user name and password on both computers
Both computers on the same domain Yes Yes Yes
Both computers on domains with two-way trust No No Yes
One or both computers on a workgroup No No Yes
Computers on different domains No No Yes

The Icing

Impersonation is usually a headache. It often involves calling a P/Invokes into the Win32 API and is often as messy as it comes. Enter the Impersonator, a class written by Uwe Keim and posted on Code Project. I tell you this little class has saved my bacon a ton of times. What I like the most about it (besides the fact that it works well) is the clear demarcation that Uwe creates between codes running on different contexts by implementing IDisposable. It's very clear which code is running on credential A, and which code is running on credential B. It's used like yo:

Console.WriteLine("User: {0}", WindowsIdentity.GetCurrent().Name); 
//Change your credentials 
using (new Impersonator("userName", "domain", "password")) 

  //now running as user "domain\userName". 
  Console.WriteLine("User: {0}", WindowsIdentity.GetCurrent().Name); 
  //Conduct your elivated privilege business. 

//Continue on as your previous credential set. 
Console.WriteLine("User: {0}", WindowsIdentity.GetCurrent().Name);

So that's pretty much it. I think that's gotta be one of the best things about being a developer, sometimes someone does all the hard work for you.

Best,
Tyler

2 comments:

Eric Paul said...

Were you using Vista? This must have been done using XP--it looks like no amount of wrangling will let you get across domains on a Vista machine.

Tyler Holmes said...

Hey Eric,

I've only ever tried it on Windows 2003/Windows XP. I've never developed on or deployed to Vista (a hole in my training for sure).

Hopefully one of these other OS' makes it possible for you to accomplish your task.

My Best,
Tyler