I'm an expert programmer, so therefore, I don't have a clue as to WTH I'm doing :)
On a serious note; no, I'm not expert by any means. I do have a problem though, and don't know how to fix it. The good thing is, I (think I) know what the problem is, and I'm hoping someone here can help.
Here's the synopsis of the problem. I am creating a form in C# that will do some server and database administration task for me. I have a button that when clicked is supposed to return the service status of "x" service on "y" server. The status is printed on the screen to a textbox.
Here's my code:
private void button2_Click(object sender, EventArgs e)
{
string fs = "Service X Status = ";
string mr = "Service A Status = ";
string qp = "Service B Status = ";
string sp = "Spooler Service Status = ";
ServiceController fssc = new ServiceController("xService", "yServer");
ServiceController mrsc = new ServiceController("aService", "yServer");
ServiceController qpsc = new ServiceController("bService", "yServer");
ServiceController spsc = new ServiceController("Spooler", "yServer");
try
{
txtGtwySts.AppendText(sp + spsc.Status.ToString());
txtGtwySts.AppendText(Environment.NewLine);
txtGtwySts.AppendText(fs + fssc.Status.ToString());
txtGtwySts.AppendText(Environment.NewLine);
txtGtwySts.AppendText(mr + mrsc.Status.ToString());
txtGtwySts.AppendText(Environment.NewLine);
txtGtwySts.AppendText(qp + qpsc.Status.ToString());
}
catch (Exception crap)
{
string msg = "";
int i;
for (i = 0; i < crap.Message.Count(); i++)
{
msg += "Error # " + i + " Message: " + crap.Message + "\n";
}
MessageBox.Show(msg);
MessageBox.Show(i.ToString());
}
}
I get exceptions, basically saying: Cannot Open "Service" on "Server." Since this is a remote server, I'm assuming this is a credential/security problem. I do NOT, however, have any problems with the Spooler service.
My question is...How can I pass userID and password to this server so that it will authenticate or runas so I can check the status of these services, it that is the problem. If someone doesnt think its the problem, then please inform me where I've went wrong :)
Finally figured it out...
Created a new class, and is shown below:
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Security.Permissions;
public class ImpersonateUser
{
[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 static IntPtr tokenHandle = new IntPtr(0);
private static WindowsImpersonationContext impersonatedUser;
// If you incorporate this code into a DLL, be sure to demand that it
// runs with FullTrust.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Impersonate(string domainName, string userName, string password)
{
//try
{
// Use the unmanaged LogonUser function to get the user token for
// the specified user, domain, and password.
const int LOGON32_PROVIDER_DEFAULT = 0;
// Passing this parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
tokenHandle = IntPtr.Zero;
// ---- Step - 1
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(
userName,
domainName,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref tokenHandle); // tokenHandle - new security token
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
// ---- Step - 2
WindowsIdentity newId = new WindowsIdentity(tokenHandle);
// ---- Step - 3
{
impersonatedUser = newId.Impersonate();
}
}
}
// Stops impersonation
public void Undo()
{
impersonatedUser.Undo();
// Free the tokens.
if (tokenHandle != IntPtr.Zero)
{
CloseHandle(tokenHandle);
}
}
}
}
and the original code that I posted is wrapped by:
ImpersonateUser iu = new ImpersonateUser();
iu.Impersonate("[domain]","[username]","[password]");
// code you want to execute as impersonated user.....
iu.Undo();
Related
Self service password reset site. For certain operations, I am impersonating a technical user that has account operator privileges in the domain. It works perfectly on my laptop, I can change a user's password, unlock the account or query the domain for all the locked accounts.
It even worked on the server until some 2 weeks ago. I tried to investigate for changes in our environment bot no one is aware of any change that could have had an effect on this.
The best part is that I have no error messages at all. Marshal.GetLastWin32Error() is returning zero, which is basically "everything went OK".
Here's the code where the impersonation happens:
#region accountManagement
[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);
WindowsImpersonationContext impersonationContext;
private bool impersonateValidUser(String userName, String domain, String password)
{
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_LOGON_BATCH = 4;
const int LOGON32_LOGON_SERVICE = 5;
const int LOGON32_LOGON_UNLOCK = 7;
const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
const int LOGON32_PROVIDER_DEFAULT = 0;
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
// Int32 result = LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token);
// Response.Write(">>> " + result.ToString());
if (LogonUserA(userName, domain, password, LOGON32_LOGON_UNLOCK, 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();
}
#endregion
And here is the piece that calls it (irrelevant parts removed):
String iuUser = System.Configuration.ConfigurationManager.AppSettings["domain.accountop.user"];
String iuPass = System.Configuration.ConfigurationManager.AppSettings["domain.accountop.pass"];
String iuDomn = System.Configuration.ConfigurationManager.AppSettings["domain.name"];
if (impersonateValidUser(iuUser, iuDomn, iuPass))
{
try
{
email = user.Properties["mail"].Value.ToString();
user.Invoke("SetPassword", new object[] { pw1 });
user.Properties["LockOutTime"].Value = 0; //unlock account
user.CommitChanges();
user.Close();
undoImpersonation();
// clear form and redirect
pPopupSuccess.Visible = true;
hfSuccess.Value = "The account is unlocked now and the password has been reset successfully.";
}
catch (Exception ex)
{
Exception innerException = ex.InnerException;
DirectoryServicesCOMException exds = (DirectoryServicesCOMException)innerException;
String errorMessage = "<p><strong>Your password probably did not meet the requirements.</strong></p>";
errorMessage += "<p>" + System.Configuration.ConfigurationManager.AppSettings["domain.pwreqmessage"] + "</p>";
errorMessage += "<strong>Detailed error message:</strong><br />";
errorMessage += ex.Message;
errorMessage += "<br />";
errorMessage += ex.StackTrace;
if (innerException != null) {
errorMessage = errorMessage + innerException.Message;
}
pPopupError.Visible = true;
hfErrorMessage.Value = errorMessage;
}
}
else
{
// The impersonation failed. Include a fail-safe mechanism here.
pPopupError.Visible = true;
hfErrorMessage.Value = "<p>Impersonation error. Failed to elevate the rights to account operator. Please report this error to the Help Desk.</p>";
hfErrorMessage.Value += "<p>" + Marshal.GetLastWin32Error().ToString() + "</p>";
}
What I keep getting is NOT an exception at the middle, but my own message at the very end of the second code piece saying the impersonation was not successful. And Marshal.GetLastWin32Error() just returs zero.
What can go wrong here and how can I get more information on what's happening? What can be different on the server that makes this code fail, while it's running OK on my dev PC?
Sorry, it was a #PEBKAC. Windows security log suggested that
The user has not been granted the requested logon type at this
machine.
So the answer is "check your windows events!"
This is strange because I had been using this server with this technical account. So I double-checked the user and put it back to the Administrators group of the server. I think it is more than I really need, also a security risk, but I will do some experiments soon to see what is the minimum amount of rights locally to keep it working.
I am writing an application which is used to manipulate the windows services. My computer is in a workgroup. But i couldn't get access to the machine. I've tried Impersonation using LogonUser(). But is not working. I am able to manipulate using Remote Desktop connection, however i couldn't access programatically.
ImpersonateUser ImpersonatedUser = new ImpersonateUser();
ImpersonatedUser.Impersonate(Domain, Username, password);
ServiceController sc = new ServiceController("servicename", host_name);
Console.WriteLine("Success.");
//impersonation.
public class ImpersonateUser
{
[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 static IntPtr tokenHandle = new IntPtr(0);
private static WindowsImpersonationContext impersonatedUser;
// If you incorporate this code into a DLL, be sure to demand that it
// runs with FullTrust.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Impersonate(string domainName, string userName, string password)
{
//try
{
// Use the unmanaged LogonUser function to get the user token for
// the specified user, domain, and password.
const int LOGON32_PROVIDER_DEFAULT = 0;
// Passing this parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
tokenHandle = IntPtr.Zero;
// ---- Step - 1
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(
userName,
domainName,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref tokenHandle); // tokenHandle - new security token
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
// ---- Step - 2
WindowsIdentity newId = new WindowsIdentity(tokenHandle);
// ---- Step - 3
{
impersonatedUser = newId.Impersonate();
}
}
}
// Stops impersonation
public void Undo()
{
impersonatedUser.Undo();
// Free the tokens.
if (tokenHandle != IntPtr.Zero)
{
CloseHandle(tokenHandle);
}
}
}
I've found that we cannot impersonate the user on workgroup computer. though we impersonate, WorkGrooup computer force the user to be guest. Thus, if we want to login as the user we need to change the registry value forceguest=0
I am impersonating a user account successfully, but I am not able to use the impersonated account to bind to AD and pull down a DirectoryEntry.
The below code outputs:
Before impersonation I am: DOMAIN\user
After impersonation I am: DOMAIN\admin
Error: C:\Users\user\ADSI_Impersonation\bin\Debug\ADSI_Impersonation.exe
samaccountname:
My issue seems similar to:
How to use the System.DirectoryServices namespace in ASP.NET
I am obtaining a primary token. I understand that I need to use delegation to use the impersonated token on a remote computer. I confirmed that the account doesn't have the flag checked "Account is sensitive and cannot be delegated". I also confirmed that the Local Group Policy and Domain Group Policies are not preventing delegation:
Computer Configuration\Windows Settings\Security Settings\Local Policies\User Rights Assignment\
What am I missing?
Thanks!
using System;
using System.DirectoryServices;
using System.Security;
using System.Security.Principal;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.Runtime.ConstrainedExecution;
namespace ADSI_Impersonation
{
class Program
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
static void Main(string[] args)
{
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
string userName = "admin#domain.com";
string password = "password";
Console.WriteLine("Before impersonation I am: " + WindowsIdentity.GetCurrent().Name);
SafeTokenHandle safeTokenHandle;
try
{
bool returnValue = LogonUser(userName, null, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
out safeTokenHandle);
if (returnValue)
{
WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle());
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
}
else
{
Console.WriteLine("Unable to create impersonatedUser.");
return;
}
}
catch (Exception e)
{
Console.WriteLine("Authentication error.\r\n" + e.Message);
}
Console.WriteLine("After impersonation I am: " + WindowsIdentity.GetCurrent().Name);
string OU = "LDAP://dc=domain,dc=com";
DirectoryEntry entry = new DirectoryEntry(OU);
entry.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher mySearcher = new DirectorySearcher();
mySearcher.SearchRoot = entry;
mySearcher.SearchScope = System.DirectoryServices.SearchScope.Subtree;
mySearcher.PropertiesToLoad.Add("cn");
mySearcher.PropertiesToLoad.Add("samaccountname");
string cn = "fistname mi. lastname";
string samaccountname = "";
try
{
// Create the LDAP query and send the request
mySearcher.Filter = "(cn=" + cn + ")";
SearchResultCollection searchresultcollection = mySearcher.FindAll();
DirectoryEntry ADentry = searchresultcollection[0].GetDirectoryEntry();
Console.WriteLine("samaccountname: " + ADentry.Properties["samaccountname"].Value.ToString());
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
}
Console.WriteLine("samaccountname: " + samaccountname);
Console.ReadLine();
}
}
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle()
: base(true)
{
}
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
}
}
Many .NET APIs do not take your manual impersonation into consideration, such as the LDAP queries you noticed. Therefore, you need to use the overloading constructors of DirectoryEntry instead,
http://msdn.microsoft.com/en-us/library/bw8k1as4.aspx
http://msdn.microsoft.com/en-us/library/wh2h7eed.aspx
Error (0x80004005): Unspecified error
I had the some problem connecting to the remote windows authenticated with the error Error (0x80004005): Unspecified error. I resolved as follows:
//Define path
//This path uses the full path of user authentication
String path = string.Format("WinNT://{0}/{1},user", server_address, username);
DirectoryEntry deBase = null;
try
{
//Try to connect with secure connection
deBase = new DirectoryEntry(path, username, _passwd, AuthenticationTypes.Secure);
//Connection test
//After test define the deBase with the parent of user (root container)
object nativeObject = deBase.NativeObject;
deBase = deBase.Parent;
}
catch (Exception ex)
{
//If an error occurred try without Secure Connection
try
{
deBase = new DirectoryEntry(path, username, _passwd);
//Connection test
//After test define the deBase with the parent of user (root container)
object nativeObject = deBase.NativeObject;
deBase = deBase.Parent;
nativeObject = deBase.NativeObject;
}
catch (Exception ex2)
{
//If an error occurred throw the error
throw ex2;
}
}
Hope that helps.
Helvio Junior
www.helviojunior.com.br
Instead of
DirectoryEntry entry = new DirectoryEntry(OU);
try
DirectoryEntry entry = new DirectoryEntry(OU, null, null, AuthenticationTypes.FastBind | AuthenticationTypes.Secure);
ASP.NET: Impersonate against a domain on VMWare
This question is what I am asking, but the answer does not provide details on how the _token is derived. It seems to only use WindowsIdentity.GetCurrent().Token so there's no impersonation happening.
Can I impersonate a user on a different Active Directory domain in .NET?
This next question has conflicting answers, with the accepted one bearing a comment "I'm beginning to suspect that my problem lies elsewhere." Not helpful.
LogonUser works only for my domain
This next question seems to imply it is not possible, but it deals with 2 domains so I am not sure if it is relevant.
My real question is:
Is it possible? And if so,
How? or Where did I go wrong?
What I have tried so far is, using the code from http://msdn.microsoft.com/en-us/library/chf6fbt4%28v=VS.80%29.aspx
bool returnValue = LogonUser(user, domain, password,
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);
// after this point, returnValue = false
The Win32 error is
Logon failure: unknown user name or bad password
Very few posts suggest using LOGON_TYPE_NEW_CREDENTIALS instead of LOGON_TYPE_NETWORK or LOGON_TYPE_INTERACTIVE. I had an impersonation issue with one machine connected to a domain and one not, and this fixed it.
The last code snippet in this post suggests that impersonating across a forest does work, but it doesn't specifically say anything about trust being set up. So this may be worth trying:
const int LOGON_TYPE_NEW_CREDENTIALS = 9;
const int LOGON32_PROVIDER_WINNT50 = 3;
bool returnValue = LogonUser(user, domain, password,
LOGON_TYPE_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50,
ref tokenHandle);
MSDN says that LOGON_TYPE_NEW_CREDENTIALS only works when using LOGON32_PROVIDER_WINNT50.
this works for me, full working example (I wish more people would do this):
//logon impersonation
using System.Runtime.InteropServices; // DllImport
using System.Security.Principal; // WindowsImpersonationContext
using System.Security.Permissions; // PermissionSetAttribute
...
class Program {
// obtains user token
[DllImport("advapi32.dll", SetLastError = true)]
public 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)]
public extern static bool CloseHandle(IntPtr handle);
public void DoWorkUnderImpersonation() {
//elevate privileges before doing file copy to handle domain security
WindowsImpersonationContext impersonationContext = null;
IntPtr userHandle = IntPtr.Zero;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
string domain = ConfigurationManager.AppSettings["ImpersonationDomain"];
string user = ConfigurationManager.AppSettings["ImpersonationUser"];
string password = ConfigurationManager.AppSettings["ImpersonationPassword"];
try {
Console.WriteLine("windows identify before impersonation: " + WindowsIdentity.GetCurrent().Name);
// if domain name was blank, assume local machine
if (domain == "")
domain = System.Environment.MachineName;
// Call LogonUser to get a token for the user
bool loggedOn = LogonUser(user,
domain,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref userHandle);
if (!loggedOn) {
Console.WriteLine("Exception impersonating user, error code: " + Marshal.GetLastWin32Error());
return;
}
// Begin impersonating the user
impersonationContext = WindowsIdentity.Impersonate(userHandle);
Console.WriteLine("Main() windows identify after impersonation: " + WindowsIdentity.GetCurrent().Name);
//run the program with elevated privileges (like file copying from a domain server)
DoWork();
} catch (Exception ex) {
Console.WriteLine("Exception impersonating user: " + ex.Message);
} finally {
// Clean up
if (impersonationContext != null) {
impersonationContext.Undo();
}
if (userHandle != IntPtr.Zero) {
CloseHandle(userHandle);
}
}
}
private void DoWork() {
//everything in here has elevated privileges
//example access files on a network share through e$
string[] files = System.IO.Directory.GetFiles(#"\\domainserver\e$\images", "*.jpg");
}
}
I was having the same problem. Don't know if you've solved this or not, but what I was really trying to do was access a network share with AD credentials. WNetAddConnection2() is what you need to use in that case.
I have been successfull at impersonating users in another domain, but only with a trust set up between the 2 domains.
var token = IntPtr.Zero;
var result = LogonUser(userID, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token);
if (result)
{
return WindowsIdentity.Impersonate(token);
}
It's better to use a SecureString:
var password = new SecureString();
var phPassword phPassword = Marshal.SecureStringToGlobalAllocUnicode(password);
IntPtr phUserToken;
LogonUser(username, domain, phPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out phUserToken);
And:
Marshal.ZeroFreeGlobalAllocUnicode(phPassword);
password.Dispose();
Function definition:
private static extern bool LogonUser(
string pszUserName,
string pszDomain,
IntPtr pszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken);
Invalid login/password could be also related to issues in your DNS server - that's what happened to me and cost me good 5 hours of my life. See if you can specify ip address instead on domain name.
The problem I encountered was when my workstation was on one domain, but I needed to authenticate to a server on a different domain:
ERROR: "Exception impersonating user, error code: 1326"
SOLUTION: Added LOGON32_LOGON_NEW_CREDENTIALS as a fallback to Impersonate/LogonUser()
Impersonation.cs
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace TestDBAccess
{
public class Impersonation : IDisposable
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String Username, String Domain, String Password, int LogonType, int LogonProvider, out IntPtr Token);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
public const int LOGON32_PROVIDER_DEFAULT = 0;
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;
private WindowsImpersonationContext impersonationContext = null;
private IntPtr userHandle = IntPtr.Zero;
public Impersonation(string user, string domain, string password)
{
// Extract domain/username from user string
string[] principal = user.Split('\\');
if (principal.Length == 2)
{
domain = principal[0];
user = principal[1];
}
if (string.IsNullOrEmpty(domain))
domain = GetDefaultDomain();
// Call LogonUser to get a token for the user
bool loggedOn =
LogonUser(user, domain, password, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, out userHandle);
if (!loggedOn)
{
int ierr = Marshal.GetLastWin32Error();
if (ierr == 1326)
{
loggedOn =
LogonUser(user, domain, password, LOGON32_LOGON_NEW_CREDENTIALS,
LOGON32_PROVIDER_DEFAULT, out userHandle);
}
if (!loggedOn)
throw new Exception("Exception impersonating user, error code: " + Marshal.GetLastWin32Error());
}
// Begin impersonating the user
impersonationContext = WindowsIdentity.Impersonate(userHandle);
}
public static string GetDefaultDomain ()
{
return System.Environment.UserDomainName;
}
public void Dispose()
{
// Clean up
if (impersonationContext != null)
impersonationContext.Undo();
if (userHandle != IntPtr.Zero)
CloseHandle(userHandle);
}
}
}
ExampleClient.cs
Impersonation Impersonation = null;
try
{
Impersonation = new Impersonation(username, null, password);
LogMsg("Attempting to connect to (" + dbInstance.instance + ")...");
using (SqlConnection connection = new SqlConnection(connString))
{
connection.Open();
string sql = edtTestSql.Text;
LogMsg("Attempting to query (" + sql + ")...");
using (SqlCommand command = new SqlCommand(sql, connection))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
LogMsg("next row: " + DumpRow(reader));
}
}
}
}
catch (Exception ex)
{
LogMsg(ex.Message);
}
finally
{
if (Impersonation != null)
Impersonation.Dispose();
}
I want to know, if a WindowsAccount is Password protected.
For security reasons, you cannot get the Password, that’s clear, but there must be a way, to find out, if a Password is set.
public bool IsAccountPasswordProteced(String userName)
{
String entryString = "WinNT://" + Environment.MachineName + ",Computer";
DirectoryEntry dirEntry = new DirectoryEntry(entryString);
DirectoryEntry user = dirEntry.Children.Find(userName, "User");
// First try was to check the ADS_UF_PASSWD_NOTREQD flag.
// If this flag is set, the account has no password,
// but if not, both is possible.
int userFlags = (int)user.Properties["UserFlags"].Value;
return (userFlags & (int)ActiveDs.ADS_USER_FLAG.ADS_UF_PASSWD_NOTREQD) == 0;
// Second try was to check the PasswordAge.
int pwAge = (int)user.Properties["PasswordAge"].Value;
return pwAge > 0;
}
If there is no better way, I will use the LogonUser-function, but that’s not the way I was looking for. But it’s better than nothing.
If I use this from a local account (not via network, because of the LogonType) and for an enabled account, it should work.
I used this two links:
Calling LogonUser
Detect empty Passwords
public bool IsAccountPasswordProtected(String userName)
{
String entryString = "WinNT://" + Environment.MachineName + ",Computer";
DirectoryEntry dirEntry = new DirectoryEntry(entryString);
DirectoryEntry user = dirEntry.Children.Find(userName, "User");
////EDIT: this flag can also be set, if the account has a password
//int userFlags = (int)user.Properties["UserFlags"].Value;
//if ((userFlags & (int)ActiveDs.ADS_USER_FLAG.ADS_UF_PASSWD_NOTREQD) != 0)
// return false;
IntPtr token;
bool result = LogonUser(
user.Name, Environment.UserDomainName,
"",
LogonTypes.Interactive,
LogonProviders.Default,
out token);
if (result)
{
CloseHandle(token);
return false;
}
else
{
int err = Marshal.GetLastWin32Error();
if (err == 1327) // ERROR_ACCOUNT_RESTRICTION
return false;
//if(err == 1331) // ERROR_ACCOUNT_DISABLED
return true;
}
}
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonTypes logonType,
LogonProviders logonProvider,
out IntPtr token);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
enum LogonTypes : uint
{
Interactive = 2,
Network,
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}
enum LogonProviders : uint
{
Default = 0, // default for platform (use this!)
WinNT35, // sends smoke signals to authority
WinNT40, // uses NTLM
WinNT50 // negotiates Kerb or NTLM
}
If you can get the UPN name or user token for the given account (one of the properties of the user object should tell you), then you should just be able to use the WindowsIdentity class something like this:
using System.Security.Principal;
// ...
var identity = new WindowsIdentity("foo-UPN");
var requiresPassword = identity.AuthenticationType != string.Empty;