Recently, I was in need of a means to move a set of files from one server to another. A straight forward task that can be accomplished with the
System.IO.File.Copy Method. Unfortunately, my situation wasn’t that simple. In my case, the source and destination servers were on different domains. The service I created was running on the source server and the
Copy Method would fail unless I could somehow authenticate the service on the destination server. The answer to my problem was Impersonation. In order to complete the file transfer I needed to impersonate a valid user context on the destination server. Thankfully, Impersonation can be accomplished by using the System.
Security.Principal .WindowsIdentity.Impersonate Method. The tricky part of this process is getting a
System.Security.Principal.WindowsIdentity for the user to impersonate on the destination domain. Fortunately, the
LogonUser Function, found in
advapi32.dll, can be used to receive a user token for an authenticated user which can be to reference the
WindowsIdentity to impersonate. Here is a sample based on the code I used:
public static class Class1
{
public enum fileaction
{
move,
copy
}
// Import the LogonUser method from the advapi32.dll
[DllImport ( "advapi32.dll", SetLastError = true )]
public static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken
);
/// <summary>
/// This method
/// </summary>
/// <param name="user">The user object to impersonate</param>
/// <param name="sourcepath">The source path for the files to action</param>
/// <param name="destpath">The destinattion path for the files to action</param>
/// <param name="searchpattern">The searchpattern to match for the files to action from the source path</param>
/// <param name="action">The action to perform on the files</param>
public static void CMIt( UserInfo user, string sourcepath, string destpath, string searchpattern, fileaction action )
{
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_LOGON_BATCH = 4;
const int LOGON32_LOGON_SERVICE = 5;
const int LOGON32_LOGON_UNLOCK = 7;
const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
try
{
IntPtr admin_token;
WindowsIdentity wid_current = WindowsIdentity.GetCurrent ();
WindowsIdentity wid_admin;
WindowsImpersonationContext wic;
//ensure a trailing backslash for the path passed
sourcepath = BackslashIt ( sourcepath );
destpath = BackslashIt ( destpath );
// Get the list of files
string[] theFilesToCopy = System.IO.Directory.GetFiles ( sourcepath, searchpattern );
// Get the user token
if ( LogonUser ( user.Username, user.Domain, user.Password, LOGON32_LOGON_NEW_CREDENTIALS, 0, out admin_token ) )
{
wid_admin = new WindowsIdentity ( admin_token );
// Impersonate the user
wic = wid_admin.Impersonate ();
foreach ( string currentFile in theFilesToCopy )
{
string filename = System.IO.Path.GetFileName ( currentFile );
string sourceFile = currentFile;
string destFile = destpath + filename;
// Perform the action
switch ( action )
{
case fileaction.move:
File.Move ( sourceFile, destFile );
break;
case fileaction.copy:
File.Copy ( sourceFile, destFile, false );
break;
default:
throw new Exception ( "Invalid action." );
}
}
wic.Undo ();
}
}
catch
{
int ret = Marshal.GetLastWin32Error ();
}
}
/// <summary>
/// This method ensures that a string has a trailing backslah
/// </summary>
/// <param name="s">A string that is to have a trailing backslash</param>
/// <returns>String with a trailing backslash</returns>
public static string BackslashIt( string s )
{
string t = s.EndsWith ( "\\" ) ? s : s + "\\";
return t;
}
}
The UserInfo object referenced is a quick class I created to hold a list of users’ credentials:
public class UserInfo
{
private string username;
public string Username
{
get
{
return username;
}
set
{
username = value;
}
}
private string password;
public string Password
{
get
{
return password;
}
set
{
password = value;
}
}
private string domain;
public string Domain
{
get
{
return domain;
}
set
{
domain = value;
}
}
public UserInfo( string username, string password, string domain )
{
this.username = username;
this.password = password;
this.domain = domain;
}
}