I'm not totally sure how you would pass credentials for IO operations, but typically I use an impersonation class to impersonate or delegate user credentials for a specific block of code.
E.g.:
/// <summary>
/// Provides a mechanism for impersonating a user. This is intended to be disposable, and
/// used in a using ( ) block.
/// </summary>
public class Impersonation : IDisposable
{
#region Externals
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken);
[DllImport("advapi32.dll", SetLastError = true)]
private extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int
SECURITY_IMPERSONATION_LEVEL, out IntPtr DuplicateTokenHandle);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
#endregion
#region Fields
private IntPtr token;
private IntPtr tokenDuplicate;
private WindowsIdentity identity;
private WindowsImpersonationContext context;
private readonly string domain;
private readonly string username;
private readonly string password;
private ImpersonationLevel level;
#endregion
#region Constructor
/// <summary>
/// Initialises a new instance of <see cref="Impersonation"/>.
/// </summary>
/// <param name="domain">The domain of the target user.</param>
/// <param name="username">The target user to impersonate.</param>
/// <param name="password">The target password of the user to impersonate.</param>
public Impersonation(string domain, string username, string password)
{
this.domain = domain;
this.username = username;
this.password = password;
this.level = ImpersonationLevel.Impersonation;
Logon();
}
/// <summary>
/// Initialises a new instance of <see cref="Impersonation"/>.
/// </summary>
/// <param name="domain">The domain of the target user.</param>
/// <param name="username">The target user to impersonate.</param>
/// <param name="password">The target password of the user to impersonate.</param>
/// <param name="level">The security level of this impersonation.</param>
public Impersonation(string domain, string username, string password, ImpersonationLevel level)
{
this.domain = domain;
this.username = username;
this.password = password;
this.level = level;
Logon();
}
#endregion
#region Methods
/// <summary>
/// Reverts the impersonation.
/// </summary>
public void Dispose()
{
if (context != null)
context.Undo();
if (token != IntPtr.Zero)
CloseHandle(token);
if (tokenDuplicate != IntPtr.Zero)
CloseHandle(tokenDuplicate);
}
/// <summary>
/// Performs the logon.
/// </summary>
private void Logon()
{
if (LogonUser(username, domain, password, 2, 0, out token))
{
if (DuplicateToken(token, (int)level, out tokenDuplicate))
{
identity = new WindowsIdentity(tokenDuplicate);
context = identity.Impersonate();
}
else
{
throw new SecurityException("Unable to impersonate the user.");
}
}
else
{
throw new SecurityException("The login details you have entered were incorrect.");
}
}
#endregion
}
/// <summary>
/// Defines the possible security levels for impersonation.
/// </summary>
public enum ImpersonationLevel
{
/// <summary>
/// Anonymous access, the process is unable to identify the security context.
/// </summary>
Anonymous = 0,
/// <summary>
/// The process can identify the security context.
/// </summary>
Identification = 1,
/// <summary>
/// The security context can be used to access local resources.
/// </summary>
Impersonation = 2,
/// <summary>
/// The security context can be used to access remote resources.
/// </summary>
Delegation = 3
}
Now, it does involve a little P/Invoke, but the end result is:
class Program
{
static void Main(string[] args)
{
using (var impersonation = new Impersonation("domain", "username", "password", ImpersonationLevel.Delegation))
{
// Do remote operations here.
}
}
}
For a given segment of code, you can impersonate the required user to perform your operations. If used in a using block, after that segment of code is executed, the impersonation is reverted and the handles closed.
If you don't use a using block, you need to ensure you call Dispose
to clear everything up.