I have a network shared drive ("\serveur\folder") on which I would like to copy file.
I can write on the drive with a specific user ("user"/"pass").
How can I access the shared drived with write privilege using C#?
Untested code, but it will be similiar to:
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
// http://pinvoke.net/default.aspx/advapi32/LogonUser.html
IntPtr token;
LogonUser("username", "domain", "password", LogonType.LOGON32_LOGON_BATCH, LogonProvider.LOGON32_PROVIDER_DEFAULT);
WindowsIdentity identity = new WindowsIdentity(token);
WindowsImpersonationContext context = identity.Impersonate();
try
{
File.Copy(#"c:\temp\MyFile.txt", #"\\server\folder\Myfile.txt", true);
}
finally
{
context.Undo();
}
Here's a working example for ASP.NET applications. Original source
private void SendToFileShare(byte[] pdfData, string fileName)
{
if(pdfData == null)
{
throw new ArgumentNullException("pdfData");
}
if (string.IsNullOrWhiteSpace(fileName))
{
//Assign a unique name because the programmer failed to specify one.
fileName = Guid.NewGuid().ToString();
}
else
{
//Should probably replace special characters (windows filenames) with something.
}
string networkShareLocation = #"\\your\network\share\";
var path = $"{networkShareLocation}{fileName}.pdf";
//Credentials for the account that has write-access. Probably best to store these in a web.config file.
var domain = "AB";
var userID = "Mr";
var password = "C";
if (ImpersonateUser(domain, userID, password) == true)
{
//write the PDF to the share:
System.IO.File.WriteAllBytes(path, report);
undoImpersonation();
}
else
{
//Could not authenticate account. Something is up.
//Log or something.
}
}
/// <summary>
/// Impersonates the given user during the session.
/// </summary>
/// <param name="domain">The domain.</param>
/// <param name="userName">Name of the user.</param>
/// <param name="password">The password.</param>
/// <returns></returns>
private bool ImpersonateUser(string domain, string userName, string password)
{
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref token) != 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;
}
/// <summary>
/// Undoes the current impersonation.
/// </summary>
private void undoImpersonation()
{
impersonationContext.Undo();
}
#region Impersionation global variables
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
WindowsImpersonationContext impersonationContext;
[DllImport("advapi32.dll")]
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);
#endregion
More simple and modern approach. Works for me in an enterprise network.
try
{
bool validLogin = false;
using (PrincipalContext tempcontext = new PrincipalContext(ContextType.Domain, "domain.company.com", null, ContextOptions.Negotiate))
{
try
{
validLogin = tempcontext.ValidateCredentials("USERNAME", "PASSWORD", ContextOptions.Negotiate);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
if (validLogin)
{
File.Copy(#"C:\folder\filename.txt", #"\\domain\folder\filename.txt", true);
return true;
}
else
{
MessageBox.Show("Username or Password is incorrect...", "Login Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return false;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return false;
}
Create a user with privileges to write to the network drive and use impersonation within the c# to use that user when accessing the drive.
Related
I have a C# windows application that I need to impersonate a userid other than the one that the user logged on with when I access files on a file share. I use impersonation to switch to this user and then switch back. I also need to revert back to the logged on user to access local files. I get a System.ExecutionEngineException that is not being caught by the application and it just stops. Here is the code. Any ideas why I am getting this error or suggestions on how to do it better.
Edit: The application is using the XAF framework by DevExpress.
public static class Impersonation
{
#region Fields
public static WindowsImpersonationContext newUser;
public static string domain;
public static string userName;
public static string password;
#endregion
// group type enum
private enum SECURITY_IMPERSONATION_LEVEL : int
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3
}
// obtains user token
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
// closes open handes returned by LogonUser
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private extern static bool CloseHandle(IntPtr handle);
// creates duplicate token handle
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
#region Methods
public static void RevertUser()
{
newUser.Undo();
}
#region ImpersonateApexApplicationUser
/// <summary>
/// Attempts to impersonate a user. If successful, returns
/// a WindowsImpersonationContext of the new users identity.
/// </summary>
/// <param name="sUsername">Username you want to impersonate</param>
/// <param name="sDomain">Logon domain</param>
/// <param name="sPassword">User's password to logon with</param></param>
/// <returns></returns>
public static void ImpersonateApexApplicationUser()
{
// initialize tokens
IntPtr pExistingTokenHandle = new IntPtr(0);
IntPtr pDuplicateTokenHandle = new IntPtr(0);
pExistingTokenHandle = IntPtr.Zero;
pDuplicateTokenHandle = IntPtr.Zero;
try
{
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
if (!LogonUser(userName, domain, Encryption.Decrypt(password), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle))
throw new Exception(string.Format("LogonUser() failed with error code {0}", Marshal.GetLastWin32Error()));
else
if (!DuplicateToken(pExistingTokenHandle, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref pDuplicateTokenHandle))
{
int errorCode = Marshal.GetLastWin32Error();
CloseHandle(pExistingTokenHandle); // close existing handle
throw new Exception(string.Format("DuplicateToken() failed with error code: {0}", errorCode));
}
else
{
// create new identity using new primary token
WindowsIdentity newId = new WindowsIdentity(pDuplicateTokenHandle);
newUser = newId.Impersonate();
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
// close handle(s)
if (pExistingTokenHandle != IntPtr.Zero)
CloseHandle(pExistingTokenHandle);
if (pDuplicateTokenHandle != IntPtr.Zero)
CloseHandle(pDuplicateTokenHandle);
}
}
#endregion
}
I call it like this once at the beginning of the program:
Impersonation.domain = "xxxx";
Impersonation.userName = "xxxx;
Impersonation.password = "xxxx";
Impersonation.ImpersonateApexApplicationUser();
and then:
Impersonation.ImpersonateApexApplicationUser();
//file share access code
Impersonation.RevertUser();
It is possible to use the logonuser function for logging onto a domain.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx
I want to logon programatically from C# onto a windows machine which is not part of any domain. How to achieve this?
I am using the following Program to logon :
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
internal void validateusercredentials(string username, string password, string hostname)
{
assert.isnotnull(username);
intptr tokenhandle = new intptr(0);
windowsidentity windowsid = null;
try
{
const int logon32_provider_default = 0;
const int logon32_logon_network = 3;
tokenhandle = intptr.zero;
bool success = logonuser(username, ".", password, logon32_logon_network,
logon32_provider_default, ref tokenhandle);
console.writeline("the return value of logon user is " + success);
if (!success)
{
int lastwindowserror = marshal.getlastwin32error();
if (lastwindowserror == error_logon_failure)
{
string message = string.format("invalid credentials supplied for user {0}", username);
console.writeline(lastwindowserror);
throw new invalidcredentialexception(message);
}
}
}
catch (exception e)
{
console.writeline(e.message);
trace.traceerror(e.message);
throw;
}
finally
{
if (tokenhandle != intptr.zero)
{
closehandle(tokenhandle);
}
if (windowsid != null)
{
windowsid.dispose();
windowsid = null;
}
}
}
If the machine is not part of your domain, you cannot use your domain credentials.
If you have a local account, you can instead use the local name of the computer as domain name and your local user and password as user and password.
I have to create many files in Network drive with specified user.
I used this answer to connect different user
I use Impersonator Class :
public class Impersonator : IDisposable
{
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
private IntPtr token = IntPtr.Zero;
private WindowsImpersonationContext impersonated;
private readonly string _ErrMsg = "";
public bool IsImpersonating
{
get { return (token != IntPtr.Zero) && (impersonated != null); }
}
public string ErrMsg
{
get { return _ErrMsg; }
}
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public Impersonator(string userName, string password, string domain)
{
StopImpersonating();
bool loggedOn = LogonUser(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token);
if (!loggedOn)
{
_ErrMsg = new System.ComponentModel.Win32Exception().Message;
return;
}
WindowsIdentity identity = new WindowsIdentity(token);
impersonated = identity.Impersonate();
}
private void StopImpersonating()
{
if (impersonated != null)
{
impersonated.Undo();
impersonated = null;
}
if (token != IntPtr.Zero)
{
CloseHandle(token);
token = IntPtr.Zero;
}
}
public void Dispose()
{
StopImpersonating();
}
}
and the code :
using (Impersonator impersonator = new Impersonator("UserName", "UserPwd", "UserDomaine"))
{
if (!Directory.Exists("Z:\\")) // check if Network drive exist
{
NetworkDrive drive = new NetworkDrive
{
ShareName = #"\\IP\Partage",
LocalDrive = "Z",
Force = true
};
drive.MapDrive(#"UserDomaine\UserName", "UserPwd");
}
File.Create(#"Z:\Log\FileName.txt");
}
But in this case I found that the code Map the drive every time that I have to create a file or update it!! And I have a lot of work with this function.
There’s a solution to not map it every time?
I tried to Map the driver in with this user in opening of application but same problem.
I think you don't need to map the drive. After impersonating you can just create the file directly using the network drive and it will create the file as impersonated user.
using (Impersonator impersonator = new Impersonator("UserName", "UserPwd", "UserDomaine"))
{
File.Create(#"\\IP\Partage\Log\FileName.txt");
}
Try not to use Using block. declare Impersonator as global static variable.
When I use WindowsIdentity Impersonation like this:
WindowsIdentity newId = null;
WindowsImpersonationContext impersonatedUser = null;
Console.WriteLine("Name of the identity BEFORE impersonation: "
+ WindowsIdentity.GetCurrent().Name + ".");
newId = new WindowsIdentity(_impersonationToken);
impersonatedUser = newId.Impersonate();
Console.WriteLine("Name of the identity AFTER impersonation: "
+ WindowsIdentity.GetCurrent().Name + ".");
(It's being used to copy files from my computer to a winCE machine.)
The Name Before and the Name after keep returning the same.
When I look # the newId Token after the impersonation it isn't the same as the one I use to Impersonate with.
The Token I impersonate with is DEFINITELY not the same user as the one I'm logged in with.
Does anyone have any suggestions on why it does not want to use my token?
(ow yeah, Yesterday It worked like a charm :s)
This is how I generate my token:
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
LogonUser(Username, IPMachine, Password,
LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
ref _token);
It give a successfull bool, so there is nothing wrong with my token i think
Here is another good example which you can try
How do you generate your _impersonationToken?
There's an excellent solution regarding impersonation over at CodeProject. Looking at that may give you some new ideas.
I get this working:
/// <summary>
/// Summary description for Impersonate
/// </summary>
public class Impersonate
{
#region "Class Members"
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
WindowsImpersonationContext _impersonationContext;
#endregion
#region "Class Properties"
private string domainName { get; set; }
private string userName { get; set; }
private string userPassword { get; set; }
#endregion
public Impersonate(string domainName, string userName, string userPassword)
{
this.domainName = domainName;
this.userName = userName;
this.userPassword = userPassword;
}
#region "Impersonation Code"
[DllImport("advapi32.dll")]
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);
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public bool ImpersonateValidUser()
{
var token = IntPtr.Zero;
var tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUserA(this.userName, this.domainName, this.userPassword, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
var 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;
}
public void UndoImpersonation()
{
_impersonationContext.Undo();
}
#endregion
}
You can call it:
var impessonate = new Impersonate(".", "User", "Psw");
if (impessonate.ImpersonateValidUser())
{
// do stuff
impessonate.UndoImpersonation();
}
I have a C# console application that needs to read a shared file on a machine in another domain.
When the application tries to access the file an exception occurs as the local user does not have permission to access the shared resource.
Currently I overcome this problem manually by open the shared folder from the run and put the username and password into the windows authentication dialog then run the application.
How can I do it programmatically?
a) p/invoke LogonUser with LOGON32_LOGON_NEW_CREDENTIALS and create a new WindowsIdentity with the new token, then use normal file access.
b) p/invoke WNetAddConnection3. Be advised that this makes your remote share accessible to every other process on your machine.
c) WMI via System.Management and CIM_DataFile; you won't even need p/invoke. System.Management lets you specify credentials for remote machine.
I used the point "a" as Anton suggested, I developed two versions for one class, the first one using the Win32 APIs, and the second uses the WindowsIdentity class.
Version 1:
class UserImpersonation : IDisposable
{
[DllImport("advapi32.dll")]
public static extern int LogonUser(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);
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
WindowsImpersonationContext wic;
string _userName;
string _domain;
string _passWord;
public UserImpersonation(string userName, string domain, string passWord)
{
_userName = userName;
_domain = domain;
_passWord = passWord;
}
public bool ImpersonateValidUser()
{
WindowsIdentity wi;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUser(_userName, _domain, _passWord, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
wi = new WindowsIdentity(tokenDuplicate);
wic = wi.Impersonate();
if (wic != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
if (tokenDuplicate != IntPtr.Zero)
{
CloseHandle(tokenDuplicate);
}
return false;
}
#region IDisposable Members
public void Dispose()
{
if (wic != null)
{
wic.Dispose();
}
RevertToSelf();
}
#endregion
}
Version2 (from MSDN with small changes):
class UserImpersonation2 : IDisposable
{
[DllImport("advapi32.dll")]
public static extern bool LogonUser(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
WindowsImpersonationContext wic;
IntPtr tokenHandle;
string _userName;
string _domain;
string _passWord;
public UserImpersonation2(string userName, string domain, string passWord)
{
_userName = userName;
_domain = domain;
_passWord = passWord;
}
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
public bool ImpersonateValidUser()
{
bool returnValue = LogonUser(_userName, _domain, _passWord,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);
Console.WriteLine("LogonUser called.");
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
Console.WriteLine("LogonUser failed with error code : {0}", ret);
return false;
}
Console.WriteLine("Did LogonUser Succeed? " + (returnValue ? "Yes" : "No"));
Console.WriteLine("Value of Windows NT token: " + tokenHandle);
// Check the identity.
Console.WriteLine("Before impersonation: "
+ WindowsIdentity.GetCurrent().Name);
// Use the token handle returned by LogonUser.
WindowsIdentity newId = new WindowsIdentity(tokenHandle);
wic = newId.Impersonate();
// Check the identity.
Console.WriteLine("After impersonation: "
+ WindowsIdentity.GetCurrent().Name);
return true;
}
#region IDisposable Members
public void Dispose()
{
if(wic!=null)
{
wic.Undo();
}
if (tokenHandle != IntPtr.Zero)
{
CloseHandle(tokenHandle);
}
}
#endregion
}
How to use (both are the same):
const string file = #"\\machine\test\file.txt";
using (UserImpersonation user = new UserImpersonation("user", "domain", "password"))
{
if (user.ImpersonateValidUser())
{
StreamReader reader = new StreamReader(file);
Console.WriteLine(reader.ReadToEnd());
reader.Close();
}
}
From memory you'll need to use a Windows API call and login as a user on the other domain. See this link for an example.
Another idea could be to use the RunAs command line argument to read the file and save it into a file on your local domain/server.