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.
Related
I am currently doing some maintenance on a C# CodedUITest. It is testing the frontend on our software which is installed in validated mode (=> which means it can only be used by the "ProtectedUser" User and Administrators).
The problem is that we obviously want to perform some clicks on the interface but we are using our "TestAgent" User who doesn't have the rights to interact with the software's interface. Instead we want to Impersonate the "ProtectedUser" User when interacting with the interface.
At some point the following code is used:
using (new TestUtils.Tools.Impersonator(".", "ProtectedUser", "password"))
{
Microsoft.VisualStudio.TestTools.UITesting.Mouse.StartDragging(SoftwareBrowserSlider);
Microsoft.VisualStudio.TestTools.UITesting.Mouse.StopDragging(SoftwareBrowserTreeView, x, y);
}
My problem is that I can't seem to get the impersonation to work to do frontend interactions such as mouse clicks or drags. But the Impersonator class works just fine when copying or deleting files. Does anyone know if it is even possible to Impersonate another local user and then perform a click or a drag as the said user?
Here is my Impersonator class:
public class Impersonator : IDisposable
{
public bool isImpersonating { get; private set; }
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
#region DllImports
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int LogonUser(
string lpszUserName,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool ImpersonateLoggedOnUser(IntPtr hToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int DuplicateToken(
IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool CloseHandle(IntPtr handle);
#endregion
public Impersonator(string domainName = ".",
string userName = "ProtectedUser",
string password = "password")
{
ImpersonateValidUser(domainName, userName, password);
}
public void Dispose()
{
UndoImpersonation();
}
private void ImpersonateValidUser(string domain, string userName, string password)
{
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if(!RevertToSelf())
{
ThrowLastWin32Exception();
}
if(LogonUser(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) == 0)
{
ThrowLastWin32Exception();
}
if (DuplicateToken(token, 2, ref tokenDuplicate) == 0)
{
ThrowLastWin32Exception();
}
isImpersonating = ImpersonateLoggedOnUser(tokenDuplicate);
}
finally
{
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
if (tokenDuplicate != IntPtr.Zero)
{
CloseHandle(tokenDuplicate);
}
}
}
private void ThrowLastWin32Exception()
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
private void UndoImpersonation()
{
if (impersonationContext != null)
{
impersonationContext.Undo();
}
}
}
I am having problems trying to read a file that is stored on a network drive when hosting my website/request on a azure website.
The code works testing on a local machine and it also works on a Dedicated server that we have with another client and have full control of but it gets the following error when we try to read the file.
Access to the path '\xxx.xxx.xxx.xxx\abc\abc.xml' is denied.
I wanted to know if Azure was locked down to prevent this behaviour that I didn't know about. Maybe ports closed or something.
The code I am using
if (impersonate.impersonateValidUser("username", "domain.com", "password"))
{
message = "impersonate ok";
if (!System.IO.File.Exists(file))
{
message = "no file";
impersonate.undoImpersonation();
}
using (StreamReader reader = File.OpenText(file))
{
message = "";
XmlSerializer deserializer = new XmlSerializer(typeof(TextModeSchema));
schema = (TextModeSchema)deserializer.Deserialize(reader);
reader.Close();
}
}
which is using a basic Impersonate class that we use in alot of places
public class Impersonate : IDisposable
{
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_LOGON_NETWORK = 3;
public const int LOGON32_LOGON_BATCH = 4;
public const int LOGON32_LOGON_SERVICE = 5;
public const int LOGON32_LOGON_UNLOCK = 7;
public const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
public const int LOGON32_PROVIDER_DEFAULT =0;
public const int LOGON32_PROVIDER_WINNT35 =1;
public const int LOGON32_PROVIDER_WINNT40 =2;
public const int LOGON32_PROVIDER_WINNT50 = 3;
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);
public bool impersonateValidUser(String userName, String domain, String password)
{
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUserA(userName, domain, password, LOGON32_LOGON_NEW_CREDENTIALS,
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;
}
public void undoImpersonation()
{
impersonationContext.Undo();
}
public void Dispose()
{
impersonationContext.Undo();
GC.Collect();
}
}
If this is not allowed on azure, does anyone know of a way we can get around it without having to set up a full Virtual machine? I would like to keep with a azure website/webapp ideally.
Is this on a web role, worker role or a web application? I know that in case of a role, the remote host in your scenario has to be on the same private network than you role (It may also be true for a web app). Also, we had a similar scenario where we decided to use Azure Files which allows mounting shares on a role which you might want to try. http://blogs.msdn.com/b/windowsazurestorage/archive/2014/05/12/introducing-microsoft-azure-file-service.aspx
I've created a window service application where it read files from other networks then save it to my database every 1 minute. I have been provided a username and password to access the file from the other network. Unfortunately, I don't know how to how to access the file with network username and. password Can you show me how. Thank you.
I know how to provide the path of the file from app.config
appSettings
add key="filepath" value="\111.111.1.11\dummyfolder\"
And access it to my code by this:
string path = ConfigurationManager.AppSettings["filepath"].ToString();
What can be easier?
class AuthHelper
{
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
[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);
public static WindowsIdentity GetWindowsIdentityForUser(String userName, String password, String domain)
{
WindowsIdentity result = null;
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)
{
result = new WindowsIdentity(tokenDuplicate);
if (result != null) return result;
}
if (token != IntPtr.Zero) CloseHandle(token);
if (tokenDuplicate != IntPtr.Zero) CloseHandle(tokenDuplicate);
return result;
}
}
And then just:
var identity = AuthHelper.GetWindowsIdentityForUser("other user", "password", "domain");
using (var context = identity.Impersonate())
{
//do whatever you want as "other user"
}
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.
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();
}