I need to verify if the user account in the LDAP is locked
I am using below code
const int ADS_UF_LOCKOUT = 0x00000010;
DirectoryEntry entry = new DirectoryEntry (_path, domainAndUsername, pwd);
if (((int)entry.Properties["useraccountcontrol"].Value & ADS_UF_LOCKOUT) == 1)
{
return true;
}
But if the user account is locked , I am receiving , "Login Failed: Bad username /password"
Please Help.
If you want to determine if a user account is locked, then you can't use the user account information that you're checking for to determine this fact - because the user account is locked, you will be denied access.
You will not be told that the reason for being unable to log on is due to the account being locked, that would be considered excessive information disclosure.
If you want to determine if the reason for not being permitted to log on is due to the account being locked then you will need an already logged on account that can check the account lock state instead of trying from the failed connection.
You can use attribute lockout time for it too: it is 0 if user is not locked. (Connect to AD using administrator credentials).
DirectoryEntry _de = new DirectoryEntry (_path, domainAdmininstratorName, pwd); // get user as directory entry object
object largeInteger = _de.Properties["lockoutTime"].Value; // it's a large integer so we need to get it's value by a little bit complex way
long highPart =
(Int32)
largeInteger.GetType()
.InvokeMember("HighPart", BindingFlags.GetProperty, null, largeInteger, null);
long lowPart =
(Int32)
largeInteger.GetType()
.InvokeMember("LowPart", BindingFlags.GetProperty, null, largeInteger, null);
long result = (long) ((uint) lowPart + (((long) highPart) << 32));
if (result == 0)
{
// account is not locked
}
else
{
// account is locked
}
Related
I have two users: SuperUser and MyUser. How can I delete some registry key for MyUser when I am logged in as SuperUser.
In my C# application I want to delete key by path: MyUser\Software\Microsoft\Windows\CurrentVersion\Run\MyApp.
Note I am runnnig my C# application when I am logged as SuperUser.
It seems that Microsoft.Win32.Registry class doesn't have a possibility to do that.
The profile may or may not be loaded. In order to ensure that it is loaded, you'll need to logon the user. Then you can load the profile and make changes. You'll need to pinvoke LogonUser() and LoadUserProfile().
// log on user
UserToken usertoken = null;
LogonUser(user, domain, pass, LogonType.LOGON32_LOGON_NETWORK,
LogonProvider.LOGON32_PROVIDER_DEFAULT, out usertoken));
// load the profile
PROFILEINFO p = new PROFILEINFO();
p.dwFlags = 1;
p.lpUserName = user;
p.dwSize = Marshal.SizeOf(p);
LoadUserProfile(usertoken, ref profile);
// get a handle to the registry
Microsoft.Win32.SafeHandles.SafeRegistryHandle saferh =
new Microsoft.Win32.SafeHandles.SafeRegistryHandle(profile.hProfile, false);
Microsoft.Win32.RegistryKey rk =
Microsoft.Win32.RegistryKey.FromHandle(m_RegHandle, Microsoft.Win32.RegistryView.Default);
I have a fairly odd requirement to be able to impersonate a user, when I'm already impersonating another, using C#.
I'm writing an app to allow the management of Active Directory users. This app will provide the ability for anyone in the company to view and maintain certain details about themselves (some of which will not actually be saved to Active Directory, but some of which will), for managers to be able to view and maintain details about their team, and for HR to be able to view and maintain details about anyone.
For obvious reasons I don't want to develop or test this against the live domain. We have recently ported all users over to this domain from another domain, which means I can actually test against the old domain without affecting anything. However, to enable me to do this I have to impersonate my old account on the old domain, which I do on loading the application.
Although for me everything will work fine as I'm setup as a domain admin, going forward obviously not all users will be domain admins, and won't be able to write to AD under their own account, and therefore we have another domain admin user setup specifically for this application, whenever data needs to be saved to AD that user is impersonated. This was working great before when I was testing against an Active Directory I'd setup on a virtual machine because I was logging onto the local domain, however that didn't allow me to step through the code in Visual Studio so debugging was slow, and hence I've stopped using that virtual machine and am using this old domain. Now I'm already impersonating another user (i.e. my old domain account), when it then tries to impersonate the domain admin user it fails with an "System.Security.SecurityException: Access is denied." exception. The line this fails on is just writing out some debugging information using "WindowsIdentity.GetCurrent().Name".
If I change my code so I'm actually logging in using the new domain admin rather than my old account, the first time it goes through it logs in successfully (so the credentials are correct), however when it then goes through and tries to do the same again to write to AD it fails with the above exception. Therefore I think it must be a problem with trying to do a nested impersonate.
Is it possible to do a nested impersonate?
Below is the code I'm using:
private static WindowsImpersonationContext ImpersonateUser(out string result, string sUsername,
string sDomain, string sPassword)
{
// initialize tokens
var pExistingTokenHandle = new IntPtr(0);
var pDuplicateTokenHandle = new IntPtr(0);
// if domain name was blank, assume local machine
if (sDomain == "")
{
sDomain = Environment.MachineName;
}
try
{
result = null;
const int logon32ProviderDefault = 0;
// create token
const int logon32LogonInteractive = 2;
// get handle to token
var bImpersonated = LogonUser(sUsername, sDomain, sPassword,
logon32LogonInteractive,
logon32ProviderDefault,
ref pExistingTokenHandle);
// did impersonation fail?
if (!bImpersonated)
{
var nErrorCode = Marshal.GetLastWin32Error();
result = "LogonUser() failed with error code: " + nErrorCode + "\r\n";
}
// Get identity before impersonation
result += string.Format("Before impersonation: {0}\r\n", WindowsIdentity.GetCurrent().Name);
var bRetVal = DuplicateToken(pExistingTokenHandle, (int)SecurityImpersonationLevel.SecurityImpersonation,
ref pDuplicateTokenHandle);
// did DuplicateToken fail?
if (bRetVal)
{
// create new identity using new primary token
var newId = new WindowsIdentity(pDuplicateTokenHandle);
var impersonatedUser = newId.Impersonate();
// check the identity after impersonation
result += "After impersonation: " + WindowsIdentity.GetCurrent().Name + "\r\n";
return impersonatedUser;
}
else
{
var nErrorCode = Marshal.GetLastWin32Error();
CloseHandle(pExistingTokenHandle); // close existing handle
result += "DuplicateToken() failed with error code: " + nErrorCode + "\r\n";
return null;
}
}
finally
{
// close handle(s)
if (pExistingTokenHandle != IntPtr.Zero)
{
CloseHandle(pExistingTokenHandle);
}
if (pDuplicateTokenHandle != IntPtr.Zero)
{
CloseHandle(pDuplicateTokenHandle);
}
}
}
When this is called for the nested impersonation which fails, "bImpersonated" is actually "true", as is bRetVal, which suggests its worked, however when it gets to "WindowsIdentity.GetCurrent().Name" it fails with the exception above.
I hope this makes sense, and would appreciate any assistance.
Question
I didn't know it would be this difficult to figure out but here I am.
I'm developing a net support client which has to detect if the current logged in user has a password set. I tried it with WMI checking the PasswordRequired property in the Win32_UserAccount class, but it returns false even if my account is password protected. I'm out of ideas...
(Background: I need this info to tell the user he has to set one so I can connect to him via remote desktop, which isn't very happy if the account is "unprotected". If there is a way to get around this I'd also accept a different solution.)
Sincerely yours
Nefarius
Solution
Easier than I thought, I managed it with the WinAPI function LogonUser and provide you this simple wrapper code:
private bool PasswordRequired
{
get
{
IntPtr phToken;
// http://www.pinvoke.net/default.aspx/advapi32/LogonUser.html
bool loggedIn = LogonUser(Environment.UserName,
null,
"",
(int)LogonType.LOGON32_LOGON_INTERACTIVE,
(int)LogonProvider.LOGON32_PROVIDER_DEFAULT,
out phToken);
int error = Marshal.GetLastWin32Error();
if (phToken != IntPtr.Zero)
// http://www.pinvoke.net/default.aspx/kernel32/CloseHandle.html
CloseHandle(phToken);
// 1327 = empty password
if (loggedIn || error == 1327)
return false;
else
return true;
}
}
That's exactly what I needed, thank you all for your fast and competent answers, I can always count on you! =)
Why not just to try to LogonUser with empty password?
Try to Change password with empty password, if succeed, that means user didn't set Password. Suppose domain user and Microsoft account always protected with password. For Microsoft account, it will throw PrincipalOperationException. For local user, if setted password, it will throw PasswordException. VB script reference, c# change password
try
{
using (var context = new PrincipalContext(ContextType.Machine))
{
var user = UserPrincipal.FindByIdentity(context, userName);
if (null == user)
{
//not local user, password required
passwordRequired = true;
}
else
{
user.ChangePassword("", "");
}
}
}
catch (PasswordException)
{
//local user password required
passwordRequired = true;
}
catch (PrincipalOperationException)
{
//for Microsoft account, password required
passwordRequired = true;
}
From what I can find, windows does not store a clear text version of the users password. Windows stores a copy that has been protected with one-way encryption. You can find more information about logging a user into windows in the MSDN documentation on LSALogonUser function. It does not help you get the users password
I need to create a new user in Active Directory. I have found several examples like the following:
using System;
using System.DirectoryServices;
namespace test {
class Program {
static void Main(string[] args) {
try {
string path = "LDAP://OU=x,DC=y,DC=com";
string username = "johndoe";
using (DirectoryEntry ou = new DirectoryEntry(path)) {
DirectoryEntry user = ou.Children.Add("CN=" + username, "user");
user.Properties["sAMAccountName"].Add(username);
ou.CommitChanges();
}
}
catch (Exception exc) {
Console.WriteLine(exc.Message);
}
}
}
}
When I run this code I get no errors, but no new user is created.
The account I'm running the test with has sufficient privileges to create a user in the target Organizational Unit.
Am I missing something (possibly some required attribute of the user object)?
Any ideas why the code does not give exceptions?
EDIT
The following worked for me:
int NORMAL_ACCOUNT = 0x200;
int PWD_NOTREQD = 0x20;
DirectoryEntry user = ou.Children.Add("CN=" + username, "user");
user.Properties["sAMAccountName"].Value = username;
user.Properties["userAccountControl"].Value = NORMAL_ACCOUNT | PWD_NOTREQD;
user.CommitChanges();
So there were actually a couple of problems:
CommitChanges must be called on user (thanks Rob)
The password policy was preventing the user to be created (thanks Marc)
I think you are calling CommitChanges on the wrong DirectoryEntry. In the MSDN documentation (http://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentries.add.aspx) it states the following (emphasis added by me)
You must call the CommitChanges method on the new entry to make the creation permanent. When you call this method, you can then set mandatory property values on the new entry. The providers each have different requirements for properties that need to be set before a call to the CommitChanges method is made. If those requirements are not met, the provider might throw an exception. Check with your provider to determine which properties must be set before committing changes.
So if you change your code to user.CommitChanges() it should work, if you need to set more properties than just the account name then you should get an exception.
Since you're currently calling CommitChanges() on the OU which hasn't been altered there will be no exceptions.
Assuming your OU path OU=x,DC=y,DC=com really exists - it should work :-)
Things to check:
you're adding a value to the "samAccountName" - why don't you just set its value:
user.Properties["sAMAccountName"].Value = username;
Otherwise you might end up with several samAccountNames - and that won't work.....
you're not setting the userAccountControl property to anything - try using:
user.Properties["userAccountControl"].Value = 512; // normal account
do you have multiple domain controllers in your org? If you, and you're using this "server-less" binding (not specifying any server in the LDAP path), you could be surprised where the user gets created :-) and it'll take several minutes up to half an hour to synchronize across the whole network
do you have a strict password policy in place? Maybe that's the problem. I recall we used to have to create the user with the "doesn't require password" option first, do a first .CommitChanges(), then create a powerful enough password, set it on the user, and remove that user option.
Marc
Check the below code
DirectoryEntry ouEntry = new DirectoryEntry("LDAP://OU=TestOU,DC=TestDomain,DC=local");
for (int i = 3; i < 6; i++)
{
try
{
DirectoryEntry childEntry = ouEntry.Children.Add("CN=TestUser" + i, "user");
childEntry.CommitChanges();
ouEntry.CommitChanges();
childEntry.Invoke("SetPassword", new object[] { "password" });
childEntry.CommitChanges();
}
catch (Exception ex)
{
}
}
How can I create a local user account using .NET 2.0 and c# and also be able to set the "Password never expires" to never.
I have tried using "Net.exe" using Process.Start and passing its parameters but it seems that the "net user" is unable to set the "Password never expires" to never.
This code will create a local account with the password never expires option set:
using System.DirectoryServices;
DirectoryEntry hostMachineDirectory = new DirectoryEntry("WinNT://localhost");
DirectoryEntries entries = hostMachineDirectory.Children;
bool userExists = false;
foreach (DirectoryEntry each in entries)
{
userExists = each.Name.Equals("NewUser",
StringComparison.CurrentCultureIgnoreCase);
if (systemtestUserExists)
break;
}
if (false == userExists)
{
DirectoryEntry obUser = entries.Add("NewUser", "User");
obUser.Properties["FullName"].Add("Local user");
obUser.Invoke("SetPassword", "abcdefg12345#");
obUser.Invoke("Put", new object[] {"UserFlags", 0x10000});
obUser.CommitChanges();
}
The 0x10000 flag means PasswordNeverExpires.
I spent a long time figuring out how to create a local user account with the password set not to expire. It seems that when you try to use:
int val = (int)newUser.Properties["userAccountControl"].Value;
newUser.Properties["userAccountControl"].Value = val | 0x10000
permissions from active directory come into play. If you have active directory permissions everything works fine. If you don't then getting the userAccountControl property will always result in a null value. Trying to set userAccountControl will result in an exception "The directory property cannot be found in the cache".
However after much hunting around I found another property "UserFlags" that needs to be set using Invoke. You can use this to set the flag on a local account. I've tried this code and it worked on windows server 2008.
Hope this helps
Read this excellent CodeProject article
Howto: (Almost) Everything In Active Directory via C#
There is a section "Create User Account" and "Dealing with User Passwords".
UPDATE:
To adapt the code for local accounts replace the respective lines with these:
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" +
Environment.MachineName);
DirectoryEntry newUser = localMachine.Children.Add("localuser", "user");
Here starts the original code snippet for domain accounts:
public string CreateUserAccount(string ldapPath, string userName,
string userPassword)
{
string oGUID = string.Empty;
try
{
string connectionPrefix = "LDAP://" + ldapPath;
DirectoryEntry dirEntry = new DirectoryEntry(connectionPrefix);
DirectoryEntry newUser = dirEntry.Children.Add
("CN=" + userName, "user");
newUser.Properties["samAccountName"].Value = userName;
int val = (int)newUser.Properties["userAccountControl"].Value;
newUser.Properties["userAccountControl"].Value = val | 0x10000;
newUser.CommitChanges();
oGUID = newUser.Guid.ToString();
newUser.Invoke("SetPassword", new object[] { userPassword });
newUser.CommitChanges();
dirEntry.Close();
newUser.Close();
}
catch (System.DirectoryServices.DirectoryServicesCOMException E)
{
//DoSomethingwith --> E.Message.ToString();
}
return oGUID;
}
There are some specifics to understand
when dealing with user passwords and
boundaries around passwords such as
forcing a user to change their
password on the next logon, denying
the user the right to change their own
passwords, setting passwords to never
expire, to when to expire, and these
tasks can be accomplished using
UserAccountControl flags that are
demonstrated in the proceeding
sections.
Please refer to this great
MSDN article: Managing User Passwords
for examples and documentation
regarding these features.
CONST HEX
------------------------------------------
SCRIPT 0x0001
ACCOUNTDISABLE 0x0002
HOMEDIR_REQUIRED 0x0008
LOCKOUT 0x0010
PASSWD_NOTREQD 0x0020
PASSWD_CANT_CHANGE 0x0040
ENCRYPTED_TEXT_PWD_ALLOWED 0x0080
TEMP_DUPLICATE_ACCOUNT 0x0100
NORMAL_ACCOUNT 0x0200
INTERDOMAIN_TRUST_ACCOUNT 0x0800
WORKSTATION_TRUST_ACCOUNT 0x1000
SERVER_TRUST_ACCOUNT 0x2000
DONT_EXPIRE_PASSWORD 0x10000
MNS_LOGON_ACCOUNT 0x20000
SMARTCARD_REQUIRED 0x40000
TRUSTED_FOR_DELEGATION 0x80000
NOT_DELEGATED 0x100000
USE_DES_KEY_ONLY 0x200000
DONT_REQ_PREAUTH 0x400000
PASSWORD_EXPIRED 0x800000
TRUSTED_TO_AUTH_FOR_DELEGATION 0x1000000
using System.DirectoryServices;
DirectoryEntry hostMachineDirectory = new DirectoryEntry("WinNT://localhost");
DirectoryEntries entries = hostMachineDirectory.Children;
bool userExists = false;
foreach (DirectoryEntry each in entries)
{
userExists = each.Name.Equals("NewUser",
StringComparison.CurrentCultureIgnoreCase);
if (systemtestUserExists)
break;
}
if (false == userExists)
{
DirectoryEntry obUser = entries.Add("NewUser", "User");
obUser.Properties["FullName"].Add("Local user");
obUser.Invoke("SetPassword", "abcdefg12345#");
obUser.Invoke("Put", new object[] {"UserFlags", 0x10000});
obUser.CommitChanges();