How do you impersonate an Active Directory user in Powershell? - c#

I'm trying to run powershell commands through a web interface (ASP.NET/C#) in order to create mailboxes/etc on Exchange 2007. When I run the page using Visual Studio (Cassini), the page loads up correctly. However, when I run it on IIS (v5.1), I get the error "unknown user name or bad password". The biggest problem that I noticed was that Powershell was logged in as ASPNET instead of my Active Directory Account. How do I force my Powershell session to be authenticated with another Active Directory Account?
Basically, the script that I have so far looks something like this:
RunspaceConfiguration rc = RunspaceConfiguration.Create();
PSSnapInException snapEx = null;
rc.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out snapEx);
Runspace runspace = RunspaceFactory.CreateRunspace(rc);
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
using (pipeline)
{
pipeline.Commands.AddScript("Get-Mailbox -identity 'user.name'");
pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
if (pipeline.Error != null && pipeline.Error.Count > 0)
{
foreach (object item in pipeline.Error.ReadToEnd())
resultString += "Error: " + item.ToString() + "\n";
}
runspace.Close();
foreach (PSObject obj in results)
resultString += obj.ToString();
}
return resultString;

Here is a class that I use to impersonate a user.
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace orr.Tools
{
#region Using directives.
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.ComponentModel;
#endregion
/// <summary>
/// Impersonation of a user. Allows to execute code under another
/// user context.
/// Please note that the account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <remarks>
/// This class is based on the information in the Microsoft knowledge base
/// article http://support.microsoft.com/default.aspx?scid=kb;en-us;Q306158
///
/// Encapsulate an instance into a using-directive like e.g.:
///
/// ...
/// using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) )
/// {
/// ...
/// [code that executes under the new context]
/// ...
/// }
/// ...
///
/// Please contact the author Uwe Keim (mailto:uwe.keim#zeta-software.de)
/// for questions regarding this class.
/// </remarks>
public class Impersonator :
IDisposable
{
#region Public methods.
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// Please note that the account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
public Impersonator(
string userName,
string domainName,
string password)
{
ImpersonateValidUser(userName, domainName, password);
}
// ------------------------------------------------------------------
#endregion
#region IDisposable member.
public void Dispose()
{
UndoImpersonation();
}
// ------------------------------------------------------------------
#endregion
#region P/Invoke.
[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", 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);
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
// ------------------------------------------------------------------
#endregion
#region Private member.
// ------------------------------------------------------------------
/// <summary>
/// Does the actual impersonation.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
private void ImpersonateValidUser(
string userName,
string domain,
string password)
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if (RevertToSelf())
{
if (LogonUser(
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();
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
finally
{
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
if (tokenDuplicate != IntPtr.Zero)
{
CloseHandle(tokenDuplicate);
}
}
}
/// <summary>
/// Reverts the impersonation.
/// </summary>
private void UndoImpersonation()
{
if (impersonationContext != null)
{
impersonationContext.Undo();
}
}
private WindowsImpersonationContext impersonationContext = null;
// ------------------------------------------------------------------
#endregion
}
}

In your ASP.NET app, you will need to impersonate a valid AD account with the correct permissions:
http://support.microsoft.com/kb/306158

Exchange 2007 doesn't allow you to impersonate a user for security reasons. This means that it is impossible (at the moment) to create mailboxes by impersonating a user. In order to get around this problem, I created a web service which runs under AD user which has permissions to create email acounts, etc. You can then access this webservice to get access to powershell. Please remember to add the necessary security because this could potentially be a huge security hole.

You might need a patch.
From: http://support.microsoft.com/kb/943937
An application cannot impersonate a
user and then run Windows PowerShell
commands in an Exchange Server 2007
environment
To resolve this problem, install
Update Rollup 1 for Exchange Server
2007 Service Pack 1.

Related

use Process.Start while Impersonating (Window Application)

I'm trying to use Process.Start() under Impersonation, i have google for few days, most answer i come across was under ASP.net, but I'm developing for Window Application, so I'm having difficulty to find the root cause.
This is my impersonate code
private void impersonateValidUser(string userName, string domain, string password)
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if ( LogonUser( userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
{
tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
mImpersonationContext = tempWindowsIdentity.Impersonate();
}
}
}
and i'm trying to open document through my program (none .exe, such as .txt, .doc)
using (new Impersonator(DomainUserID, Domain, Password))
Process.Start(filePath);
So far I'm able to detect the directory/file with the impersonate user, which suppose to be invisible to my current login user since i did not grant it the access. But whenever i try to open the document, i get error
System.ComponentModel.Win32Exception (0x80004005): Access is denied
Please correct me if I'm wrong, so in this scenario, I'm not suppose to set the UseShellExecute to false (and processStartInfo with username and password input too?) because it's for executable file(?), and i do come across with CreateProcessAsUser function as well, and this function doesn't seems to be applicable to my case too, from the example i read it's for the .exe file too.
Would be appreciate if anyone could enlightenment me.
update: impersonate class
public class Impersonator : IDisposable
{
#region P/Invoke
[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", 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
#region Constants
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
#endregion
#region Attributes
private WindowsImpersonationContext mImpersonationContext = null;
#endregion
#region Public methods.
public Impersonator( string userName, string domainName, string password)
{
impersonateValidUser(userName, domainName, password);
}
#endregion
#region IDisposable member.
public void Dispose()
{
undoImpersonation();
}
#endregion
#region Private member.
private void impersonateValidUser(string userName, string domain, string password)
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if ( RevertToSelf() )
{
if ( LogonUser( userName, domain, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
{
tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
mImpersonationContext = tempWindowsIdentity.Impersonate();
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
finally
{
if ( token != IntPtr.Zero )
{
CloseHandle( token );
}
if ( tokenDuplicate != IntPtr.Zero )
{
CloseHandle( tokenDuplicate );
}
}
}
/// <summary>
/// Reverts the impersonation.
/// </summary>
private void undoImpersonation()
{
if ( mImpersonationContext != null )
{
mImpersonationContext.Undo();
}
}
#endregion
}
You can't use UseShellExecute = true when impersonating. This is related to the way how shell execution works in Windows. Basically everything is passed to the shell which looks up how to handle the verb ("open" in your case) and then starts the application under the user owning the shell, which is not the impersonated user - the impersonated user doesn't actually have a shell if there is no session!
Although you use a different mechanism for impersonating a user the documentation for UseShellExecute still applies in your case:
UseShellExecute must be false if the UserName property is not null or an empty string, or an InvalidOperationException will be thrown when the Process.Start(ProcessStartInfo) method is called.
To solve this issue it might be the easiest to look up the registered application yourself as described in this answer: Finding the default application for opening a particular file type on Windows. With the path to the associated application you can then start the executable as the other user.
Here is the complete class I use to impersonate user:
/// <summary>
/// Provide a context to impersonate operations.
/// </summary>
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public class Impersonate : IDisposable
{
/// <summary>
/// Initialize a new instance of the ImpersonateValidUser class with the specified user name, password, and domain.
/// </summary>
/// <param name="userName">The user name associated with the impersonation.</param>
/// <param name="password">The password for the user name associated with the impersonation.</param>
/// <param name="domain">The domain associated with the impersonation.</param>
/// <exception cref="ArgumentException">If the logon operation failed.</exception>
public Impersonate(string userName, SecureString password, string domain)
{
ValidateParameters(userName, password, domain);
WindowsIdentity tempWindowsIdentity;
IntPtr userAccountToken = IntPtr.Zero;
IntPtr passwordPointer = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if (Kernel32.RevertToSelf())
{
// Marshal the SecureString to unmanaged memory.
passwordPointer = Marshal.SecureStringToGlobalAllocUnicode(password);
if (Advapi32.LogonUser(userName, domain, passwordPointer, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref userAccountToken))
{
if (Advapi32.DuplicateToken(userAccountToken, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
return;
}
}
}
}
}
finally
{
// Zero-out and free the unmanaged string reference.
Marshal.ZeroFreeGlobalAllocUnicode(passwordPointer);
// Close the token handle.
if (userAccountToken != IntPtr.Zero)
Kernel32.CloseHandle(userAccountToken);
if (tokenDuplicate != IntPtr.Zero)
Kernel32.CloseHandle(tokenDuplicate);
}
throw new ArgumentException(string.Format("Logon operation failed for userName {0}.", userName));
}
/// <summary>
/// Reverts the user context to the Windows user represented by the WindowsImpersonationContext.
/// </summary>
public void UndoImpersonation()
{
impersonationContext.Undo();
}
/// <summary>
/// Releases all resources used by <see cref="Impersonate"/> :
/// - Reverts the user context to the Windows user represented by this object : <see cref="System.Security.Principal.WindowsImpersonationContext"/>.Undo().
/// - Dispose the <see cref="System.Security.Principal.WindowsImpersonationContext"/>.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (impersonationContext != null)
{
//UndoImpersonation();
impersonationContext.Dispose();
impersonationContext = null;
}
}
}
private void ValidateParameters(string userName, SecureString password, string domain)
{
if (string.IsNullOrWhiteSpace(userName))
{
throw new ArgumentNullException("userName");
}
if (password == null || password.Length == 0)
{
throw new ArgumentNullException("password");
}
if (string.IsNullOrWhiteSpace(userName))
{
throw new ArgumentNullException("domain");
}
}
private WindowsImpersonationContext impersonationContext;
private const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
}
how to convert a string to a secure string :
var secure = new SecureString();
foreach (char c in "myclearpassword")
{
secure.AppendChar(c);
}
SO You can use it like that:
using (var imp = new Impersonate(DomainUserID, SecurePassword, Domain))
{
Process.Start(filePath);
}

Logon failure: the user has not been granted the requested logon type at this computer

I have a website in which I create a file and save it in a remote share folder. I have a account which have write access to that share folder. So I impersonate that account while saving the file in that path. This logic works when I run the application from my visual studio locally in my machine. When I deploy the code in the development server, It does't work. I get the following error.
"Logon failure: the user has not been granted the requested logon type at this computer"
can someone help.
Here is the impersonation code from codeproject
using System;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.ComponentModel;
public class Impersonator :
IDisposable
{
public Impersonator(
string userName,
string domainName,
string password)
{
ImpersonateValidUser(userName, domainName, password);
}
// ------------------------------------------------------------------
#endregion
#region IDisposable member.
// ------------------------------------------------------------------
public void Dispose()
{
UndoImpersonation();
}
// ------------------------------------------------------------------
#endregion
#region P/Invoke.
// ------------------------------------------------------------------
[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", 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);
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
// ------------------------------------------------------------------
#endregion
#region Private member.
// ------------------------------------------------------------------
/// <summary>
/// Does the actual impersonation.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
private void ImpersonateValidUser(
string userName,
string domain,
string password)
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if (RevertToSelf())
{
if (LogonUser(
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();
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
finally
{
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
if (tokenDuplicate != IntPtr.Zero)
{
CloseHandle(tokenDuplicate);
}
}
}
/// <summary>
/// Reverts the impersonation.
/// </summary>
private void UndoImpersonation()
{
if (impersonationContext != null)
{
impersonationContext.Undo();
}
}
private WindowsImpersonationContext impersonationContext = null;
// ------------------------------------------------------------------
#endregion
}
I found the answer myself. I changed the logon type to 8 and it worked.. Thanks everyone

Invoke or call C# console app from C# web service?

Due to my problem that I am unable to run dos command via my web service.
C# web service running batch file or dos command?
Since I cannot make my web service run dos command directly, I am now thinking about creating C# console app that will run my dos command, then the console app will be invoked by web service.
Is it possible to do so?
If it's possible from within a web service, you'll need to do one of two things:
Use impersonation to execute the console application
Use an account in IIS that can execute the application.
Assuming that one of the above works and you're able to execute the console app, there are also a few other things you'll need to look into:
You may need to change the execute permissions under the Home Directory tab in IIS
You may need to suppress the console dialog, as this may raise some flags
I had to do this once before, and standard impersonation didn't work for me. I had to handle impersonation a little differently. I don't know if you'll run into the same obstacles that I did, but here is a class that I created for impersonating programmatically through the Windows API:
EDIT
Changed to an instance class implementing IDisposable - thanks #John Saunders
Added an overloaded constructor for easier implementation with using statement, and added boolean property Impersonating to check whether the instance is currently impersonating. There are also BeginImpersonationContext and EndImpersonationContext methods for alternative use.
/// <summary>
/// Leverages the Windows API (advapi32.dll) to programmatically impersonate a user.
/// </summary>
public class ImpersonationContext : IDisposable
{
#region constants
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
#endregion
#region global variables
private WindowsImpersonationContext impersonationContext;
private bool impersonating;
#endregion
#region unmanaged code
[DllImport("advapi32.dll")]
private 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)]
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
#region constructors
public ImpersonationContext()
{
impersonating = false;
}
/// <summary>
/// Overloaded constructor and begins impersonating.
/// </summary>
public ImpersonationContext(string userName, string password, string domain)
{
this.BeginImpersonationContext(userName, password, domain);
}
#endregion
#region impersonation methods
/// <summary>
/// Begins the impersonation context for the specified user.
/// </summary>
/// <remarks>Don't call this method if you used the overloaded constructor.</remarks>
public void BeginImpersonationContext(string userName, string password, string domain)
{
//initialize token and duplicate variables
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)
{
using (WindowsIdentity tempWindowsIdentity = new WindowsIdentity(tokenDuplicate))
{
//begin the impersonation context and mark impersonating true
impersonationContext = tempWindowsIdentity.Impersonate();
impersonating = true;
}
}
}
}
//close the handle to the account token
if (token != IntPtr.Zero)
CloseHandle(token);
//close the handle to the duplicated account token
if (tokenDuplicate != IntPtr.Zero)
CloseHandle(tokenDuplicate);
}
/// <summary>
/// Ends the current impersonation context.
/// </summary>
public void EndImpersonationContext()
{
//if the context exists undo it and dispose of the object
if (impersonationContext != null)
{
//end the impersonation context and dispose of the object
impersonationContext.Undo();
impersonationContext.Dispose();
}
//mark the impersonation flag false
impersonating = false;
}
#endregion
#region properties
/// <summary>
/// Gets a value indicating whether the impersonation is currently active.
/// </summary>
public bool Impersonating
{
get
{
return impersonating;
}
}
#endregion
#region IDisposable implementation
~ImpersonationContext()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (impersonationContext != null)
{
impersonationContext.Undo();
impersonationContext.Dispose();
}
}
}
#endregion
}
EDIT
Lastly, here is an example of how to implement the class:
using (ImpersonationContext context = new ImpersonationContext("user", "password", "domain"))
{
if (context.Impersonating)
{
Process.Start(#"/Support/SendFax/SendFax.exe");
}
}
The easiest method to call a .Net console application from another .Net application is to link in the assembly, and invoke the static Main method directly. But if there is any reason you can't execute commands (permissions), then you'll have the same problems with this method.
If permissions are the problem, then you could:
Change the account ASP.Net uses to host your web service
Create a Windows service that hosts a WCF or .Net Remoting listener. Design the listener to spawn the processes you need to run. Run that service with the permissions you require
Also, as John Kalberer mentioned, it could be related to the inability of these services to interact with the desktop. If this is the problem, then see this question.

Dynamically Impersonate a remote user - c# and asp.net

I want to impersonate a remote user dynamically for executing some part of code. I searchd a lot in net and got some codes to impersonate. The code to impersonate is shown below
namespace Tools
{
#region Using directives.
// ----------------------------------------------------------------------
using System;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.ComponentModel;
// ----------------------------------------------------------------------
#endregion
/////////////////////////////////////////////////////////////////////////
/// <summary>
/// Impersonation of a user. Allows to execute code under another
/// user context.
/// Please note that the account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <remarks>
/// This class is based on the information in the Microsoft knowledge base
/// article http://support.microsoft.com/default.aspx?scid=kb;en-us;Q306158
///
/// Encapsulate an instance into a using-directive like e.g.:
///
/// ...
/// using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) )
/// {
/// ...
/// [code that executes under the new context]
/// ...
/// }
/// ...
///
/// Please contact the author Uwe Keim (mailto:uwe.keim#zeta-software.de)
/// for questions regarding this class.
/// </remarks>
public class Impersonator :
IDisposable
{
#region Public methods.
// ------------------------------------------------------------------
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// Please note that the account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
public Impersonator(
string userName,
string domainName,
string password )
{
ImpersonateValidUser( userName, domainName, password );
}
// ------------------------------------------------------------------
#endregion
#region IDisposable member.
// ------------------------------------------------------------------
public void Dispose()
{
UndoImpersonation();
}
// ------------------------------------------------------------------
#endregion
#region P/Invoke.
// ------------------------------------------------------------------
[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", 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);
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
// ------------------------------------------------------------------
#endregion
#region Private member.
// ------------------------------------------------------------------
/// <summary>
/// Does the actual impersonation.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
private void ImpersonateValidUser(
string userName,
string domain,
string password )
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if ( RevertToSelf() )
{
if ( LogonUser(
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();
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
finally
{
if ( token!= IntPtr.Zero )
{
CloseHandle( token );
}
if ( tokenDuplicate!=IntPtr.Zero )
{
CloseHandle( tokenDuplicate );
}
}
}
/// <summary>
/// Reverts the impersonation.
/// </summary>
private void UndoImpersonation()
{
if ( impersonationContext!=null )
{
impersonationContext.Undo();
}
}
private WindowsImpersonationContext impersonationContext = null;
// ------------------------------------------------------------------
#endregion
}
/////////////////////////////////////////////////////////////////////////
}
and the code which calls the above functions is shown below
using System;
using System.IO;
using Tools;
namespace ImpersonatorDemo
{
/// <summary>
/// Main class for the demo application.
/// Call this application with a low privileg account to test.
/// </summary>
class Program
{
/// <summary>
/// The main entry point.
/// </summary>
[STAThread]
static void Main( string[] args )
{
// Impersonate, automatically release the impersonation. format is new Impersonator( "username", "domain", "password" )
using ( new Impersonator( "TestUser", "MachineA", "admin" ) )
{
string name = Environment.UserDomainName;
string s = Environment.UserName;
// The following code is executed under the impersonated user.
string[] files = Directory.GetFiles( "c:\\" );
}
}
}
}
It works fine if i try to impersonate user on the local machine. But if i tried to imporsenate a user on the remote machine it always throws an error .which is shown below
Logon failure: unknown user name or bad password
in the remote machine there is a user named Testuser and password is admin and machine name is MachineA ( is thi is domain name??) and the ip address is 192.168.0.33 . the workgroup is myWorkGroup .I tried to impersonate users on many mremote machine . But it always shows the same error i wrote above if i am tryied to impersonate a remote user. and i am sure my local machine is connected to the network
As I understand your problem, the ImpersonateDemo code above runs on your server (ServerA).
ServerA tries to get a list of files on a remote machine (MachineA).
If your code on ServerA requests the files in the C:\ directory you are always going to get the files on your server's C drive.
That is why when you impersonate a local user (on ServerA) it works - because the user is on the machine that the drive is on. When you impersonate with the remote user (from MachineA) you are still trying to get the contents of ServerA's C drive, but that user doesn't exist on ServerA.
You need to request files based on the IP address (or machine name) of the remote machine. If the TestUser has permission to read MachineA's C drive, then try this:
string[] files = Directory.GetFiles( #"\\192.168.0.33\C$" );
string[] files = Directory.GetFiles( #"\\MachineA\C$" );
This looks like it is a manifestation of the double hop problem:
Link
Impersonate is not as simple as it looks like. While the code to impersonate is pretty simple, finding the arguments may be not. It depends on the way you need to authenticate your user -> a workgroup differs from a domain differs from a machine, etc...
For your workgroup it matters if the user is know on both machines or not. As a simple technical approach you should try leaving the domain/machine name blank.Also check Ricks post, the double hop stuff is quite annoying when woring with impersonation :(

Using C# to pass server share credentials prior to operations

my application use a shared directory (Windows) on the network and i want to make that share available on network only to a given user and password.
Is there any way to pass authentication prior to any operations on this network resource? *I'm not using a domain.
Thank you so much!
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.

Categories

Resources