Create a new logon session by calling LogonUser (advapi32.dll) using the
remote credentials (user, password and machinename)with logon type =
LOGON32_LOGON_NEW_CREDENTIALS (int value=9). Create a new WindowsIdentity
using the token returned from LogonUser and impersonate that new identity
when you need to access the remote resource. The new impersonated token is
used to access the remote share only , all local resources are still using
the current users token.
Please read the SDK doc's remarks on LogonUser's dwLogonType, this option
requires.
Willy.
It worked! Below is what I came up with. Note that in my situation
192.19.12.46 is not even a Windows machine, yet an AS/400 server.
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.IO;
namespace RemoteCredentials
{
class MainClass
{
[DllImport( "advapi32.dll", SetLastError = true )]
private static extern bool LogonUser( string
lpszUsername, string lpszDomain, string lpszPassword,
int dwLogonType, int dwLogonProvider, ref
IntPtr phToken );
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private unsafe static extern int FormatMessage(int
dwFlags, ref IntPtr lpSource,
int dwMessageId, int dwLanguageId, ref String
lpBuffer, int nSize, IntPtr *arguments);
[DllImport( "kernel32.dll", CharSet = CharSet.Auto,
SetLastError = true )]
private static extern bool CloseHandle( IntPtr handle
);
[DllImport("advapi32.dll", CharSet = CharSet.Auto,
SetLastError = true)]
public extern static bool DuplicateToken( IntPtr
existingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr
duplicateTokenHandle );
// logon types
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
// logon providers
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_PROVIDER_WINNT50 = 3;
const int LOGON32_PROVIDER_WINNT40 = 2;
const int LOGON32_PROVIDER_WINNT35 = 1;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
IntPtr token = IntPtr.Zero;
IntPtr dupToken = IntPtr.Zero;
bool isSuccess = LogonUser( "userId",
"192.19.12.46", "password",
LOGON32_LOGON_NEW_CREDENTIALS,
LOGON32_PROVIDER_DEFAULT, ref token );
if( !isSuccess )
{
RaiseLastError();
}
isSuccess = DuplicateToken( token, 2, ref
dupToken );
if( !isSuccess )
{
RaiseLastError();
}
WindowsIdentity newIdentity = new
WindowsIdentity( dupToken );
WindowsImpersonationContext impersonatedUser =
newIdentity.Impersonate();
DirectoryInfo dirInfo = new DirectoryInfo(
@"\\192.19.12.46\sharedfolder" );
FileInfo[] files = dirInfo.GetFiles();
impersonatedUser.Undo();
foreach( FileInfo file in files )
{
Console.WriteLine( file.FullName );
}
isSuccess = CloseHandle( token );
if( !isSuccess )
{
RaiseLastError();
}
}
// GetErrorMessage formats and returns an error
message
// corresponding to the input errorCode.
public unsafe static string GetErrorMessage( int
errorCode )
{
int FORMAT_MESSAGE_ALLOCATE_BUFFER =
0x00000100;
int FORMAT_MESSAGE_IGNORE_INSERTS =
0x00000200;
int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
int messageSize = 255;
string lpMsgBuf = "";
int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
IntPtr ptrlpSource = IntPtr.Zero;
IntPtr ptrArguments = IntPtr.Zero;
int retVal = FormatMessage(dwFlags, ref
ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, &ptrArguments);
if( retVal == 0 )
{
throw new ApplicationException(
string.Format( "Failed to
format message for error code '{0}'.", errorCode ) );
}
return lpMsgBuf;
}
private static void RaiseLastError()
{
int errorCode = Marshal.GetLastWin32Error();
string errorMessage = GetErrorMessage(
errorCode );
throw new ApplicationException( errorMessage
);
}
}
}