The problem:
I have windows service that uses impersonation to check the service status on a separate server, however it was noted during testing that when the user supplied a local machine address and an invalid local machine account the check would proceed to open the service control manager and retrieve the status successfully. We aim to have it only work with a valid local machine account.
The Code:
Impersonation namespace (contains method (SoddingNetworkAuth) used to set up the impersonation:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace Impersonation
{
public class Network
{
public class SoddingNetworkAuth : IDisposable
{
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
[DllImport("kernel32", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
private IntPtr userHandle = IntPtr.Zero;
private WindowsImpersonationContext impersonationContext;
public SoddingNetworkAuth(string user, string domain, string password)
{
if (!string.IsNullOrEmpty(user))
{
// Call LogonUser to get a token for the user
bool loggedOn = LogonUser(user, domain, password,
9 /*(int)LogonType.LOGON32_LOGON_NEW_CREDENTIALS*/,
3 /*(int)LogonProvider.LOGON32_PROVIDER_WINNT50*/,
out userHandle);
if (!loggedOn)
throw new Win32Exception(Marshal.GetLastWin32Error());
// Begin impersonating the user
impersonationContext = WindowsIdentity.Impersonate(userHandle);
}
}
public void Dispose()
{
if (userHandle != IntPtr.Zero)
CloseHandle(userHandle);
if (impersonationContext != null)
impersonationContext.Undo();
}
}
}
}
A console app I wrote for the test:
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceProcess;
namespace NetworkAuthIssue
{
class Program
{
static void Main(string[] args)
{
string result;
try
{
using (new Impersonation.Network.SoddingNetworkAuth("Invalid User", "localhost", "InvalidPassword"))
{
var serviceController = new ServiceController("Power", "localhost");
if (serviceController.Status == ServiceControllerStatus.Running)
{
result = "Authenticated, service running";
}
else
{
result = "Authenticated, service not running";
}
}
}
catch (Exception e)
{
result = "Authentication Failed";
}
Console.WriteLine(result);
Console.Read();
}
}
}
I am running Windows 8.1
It's because you're using LOGON32_LOGON_NEW_CREDENTIALS.
From https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184%28v=vs.85%29.aspx (emphasis mine):
This logon type allows the caller to clone its current token and specify new credentials for outbound connections. The new logon session has the same local identifier but uses different credentials for other network connections.
The "outbound connections" part is important. It means that for local connections, the provided credentials aren't used at all. The current identity gets used for local connections.
I can't check things right now but I had similar issues like you and this was caused by the selected LogonType/LogonProvider values in the Logon method. You should try them out in a console app and use the combination which works for your scenario.
See also the msdn documentation for this method: https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx
Related
I have an application that runs with the system user. I need that some code in the app runs on user context (with the user itself). For example i need to use the webclient in user context because that way my firewall recognize the username, but later i need to run some other code ex. Running a exe in system context (with system user) because i need the privileges.
Is there a way to do this?.
Thanks!!!
I have used this code to impersonate certain operations
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;
using NLog;
namespace Core.Impersonation
{
public class ImpersonatedUser : IDisposable
{
private static Logger logger = LogManager.GetCurrentClassLogger();
IntPtr userHandle;
WindowsImpersonationContext impersonationContext;
public ImpersonatedUser(string user, string domain, string password)
{
logger.Debug("Impersonating user: " + domain + #"\" + user);
userHandle = IntPtr.Zero;
bool loggedOn = LogonUser(
user,
domain,
password,
LogonType.Interactive,
LogonProvider.Default,
out userHandle);
if (!loggedOn)
throw new Win32Exception(Marshal.GetLastWin32Error());
// Begin impersonating the user
impersonationContext = WindowsIdentity.Impersonate(userHandle);
}
public void Dispose()
{
if (userHandle != IntPtr.Zero)
{
CloseHandle(userHandle);
userHandle = IntPtr.Zero;
impersonationContext.Undo();
logger.Debug("Finished Impersonating user");
}
}
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
LogonType dwLogonType,
LogonProvider dwLogonProvider,
out IntPtr phToken
);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hHandle);
enum LogonType : int
{
Interactive = 2,
Network = 3,
Batch = 4,
Service = 5,
NetworkCleartext = 8,
NewCredentials = 9,
}
enum LogonProvider : int
{
Default = 0,
}
}
}
and then usage:
using (var i = new ImpersonatedUser("someLogin", "someDomain", "thePassword"))
{
var u = System.Environment.UserName;
}
I'm very new to c# and I want to do a code that will logoff all users that logged to the pc.
I tried to do this:
System.Diagnostics.Process("shutdown", "/l");
But this didn't logged off all the users, this logged off only the user that I was running from it.
So how can I do this?
Use the WTSDisconnectSession() Windows API. See article here.
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
class Program
{
[DllImport("wtsapi32.dll", SetLastError = true)]
static extern bool WTSDisconnectSession(IntPtr hServer, int sessionId, bool bWait);
const int WTS_CURRENT_SESSION = -1;
static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
static void Main(string[] args)
{
if (!WTSDisconnectSession(WTS_CURRENT_SERVER_HANDLE,
WTS_CURRENT_SESSION, false))
throw new Win32Exception();
}
}
This theme are not new. But I need help of some professional.
I am making a windows form application which will run on local system(not domain's computer). The application would creating folder and some files on Active Directory domain's shared folder.
I have read about Impersonation and have tried to make some thing like this: Impersonation by using LogonUser
Then I wrote the next code:
using System.Security.Principal;
using System.Runtime.InteropServices;
public partial class Form1 : Form
{
private enum LogonSessionType : uint
{
Interactive = 2,
Network,
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}
private enum LogonProvider : uint
{
Default = 0,
WinNT35,
WinNT40,
WinNT50
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonSessionType logonType,
LogonProvider logonProvider,
out IntPtr token);
public Form1()
{
InitializeComponent();
}
protected void btnConnect_Click(object sender, EventArgs e)
{
IntPtr token = IntPtr.Zero;
WindowsImpersonationContext impersonateUser = null;
try
{
bool result = LogonUser("Administrator#mydomain.ru", "192.168.1.1", "SomeP#ssWorD",
LogonSessionType.Network, LogonProvider.Default, out token);
if(result)
{
WindowsIdentity id = new WindowsIdentity(token);
impersonateUser = id.Impersonate();
string showtext = string.Format("Identity: {0}", WindowsIdentity.GetCurrent().Name);
MessageBox.Show(showtext);
}
else
{
string showtext = string.Format("Identity: {0}", "Fail");
MessageBox.Show(showtext);
}
}
catch
{
}
finally
{
if(impersonateUser!=null)
impersonateUser.Undo();
if (token != IntPtr.Zero)
CloseHandle(token);
}
}
}
But bool result is always = false. What am I doing wrong?
i was wrong about understanding of LogonUser function. I was thinking that this function get remote token, but it generate.
Here is the right using:
bool result = LogonUser("Administrator", "mydomain.ru", "SomePa$$worD", LogonSessionType.NewCredentials, LogonProvider.Default, out safeTokenHandle);
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.
My ASP.NET 4.0 Web App is unable to access Network Printers, while debugging on VS 2010. It can access local printers. Seems like it may be a permissions issue. Since VS2010 Debugging runs on ASP.NET Development Server, it must be running under the account I used to log into Windows, right? Does that user need to be added as an Admin in that printers users? Is there any account that I can impersonate to get this working?
You are correct, debugging through Visual Studio means all of your code runs with the same rights as the user logged in to windows. On the server, you will need to setup impersonation and/or setup your Application Pool to run as a user who has access to print on these printers.
I recommend you setup a dedicated domain account (like domain\yourapp-impers-user) and either set the Application Pool to use that, or setup impersonation in your web.config. Then on the print server, you simply grant that user account the necessary permissions.
Here is an example of impersonation
using System;
using System.Collections.Generic;
using System.Web.Security;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Linq;
using System.Web;
using System.IO;
public partial class Main : PageBase
{
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);
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack == false)
{
if (impersonateValidUser("Username", "Domain", "Password"))
{
//Your code under a specific user here.
}
}
}
private 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_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;
}
private void undoImpersonation()
{
impersonationContext.Undo();
}
Good luck !
without knowing for sure, you can try to impersonate the Network Service, or of course your own domain account which should always work.