I'm validating user logins using python's ldap module. When the login fails, I get a ldap.INVALID_CREDENTIALS login, but this can be either because of a wrong password or because the account is locked. The account get's locked after the 3rd try.
I would like to detect that the account is locked and report that to the frustrated user, instead of the same "invalid login" message.
Searching for a solution I found:
The LDAP query I should be using to find locked users is:
(&(objectClass=user)(lockoutTime>=1))
Or for a specific user:
(&(objectClass=user)(sAMAccountName=jabberwocky)(lockoutTime>=1))
But this is not working, the query returns no results every time.
Check AD account lockout status In ADUC, navigate to the properties of the user, then the Account tab. You will see the following message if an account is locked out: Unlock account. This account is currently locked out on this Active Directory Domain Controller.
UserAccountControl value 544 means that the account is enabled but must to change password on next logon.
Usually, the account is locked by the domain controller for several minutes (5-30), during which the user can't log in to the AD domain. After some time (set by domain security policy), the user account is automatically unlocked. Temporary AD account lockout reduces the risk of brute force attacks to AD user accounts.
A value of zero in lockoutTime
means it's not locked out. So, you should try this.
(&(objectClass=user)(!lockoutTime=0))
Actually, the above query is still not 100% correct. If you read the fine print from MSDN, Microsoft is suggesting you to add the Lockout-Time
attribute to the Lockout-Duration
attribute and then compare it with the current time. That's because there is such a thing called lockout duration. Once the lockout duration passes, the user is unlocked automatically. Zero in Lockout-Duration
means the account is locked forever until the administrator unlock it.
See this MSDN article
This attribute value is only reset when the account is logged onto successfully. This means that this value may be non zero, yet the account is not locked out. To accurately determine if the account is locked out, you must add the Lockout-Duration to this time and compare the result to the current time, accounting for local time zones and daylight savings time.
lockoutTime
is a <not set>
attribute so the easiest way is to use:
(&(objectClass=user)(lockoutDuration=*)))
for the non-empty entries.
Update:
However, this value is also set when the password expires, password needs to change etc.
So it needs to be filtered by:
UserPrincipal userPrincipal = new UserPrincipal(context);
bool isLocked = userPrincipal.IsAccountLockedOut();
to get the cases where the user is locked out because they violated the password policy e.g incorrectly entered the password 5 times.
I found also this list of property flags: How to use the UserAccountControl flags
SCRIPT 0x0001 1
ACCOUNTDISABLE 0x0002 2
HOMEDIR_REQUIRED 0x0008 8
LOCKOUT 0x0010 16
PASSWD_NOTREQD 0x0020 32
PASSWD_CANT_CHANGE 0x0040 64
ENCRYPTED_TEXT_PWD_ALLOWED 0x0080 128
TEMP_DUPLICATE_ACCOUNT 0x0100 256
NORMAL_ACCOUNT 0x0200 512
INTERDOMAIN_TRUST_ACCOUNT 0x0800 2048
WORKSTATION_TRUST_ACCOUNT 0x1000 4096
SERVER_TRUST_ACCOUNT 0x2000 8192
DONT_EXPIRE_PASSWORD 0x10000 65536
MNS_LOGON_ACCOUNT 0x20000 131072
SMARTCARD_REQUIRED 0x40000 262144
TRUSTED_FOR_DELEGATION 0x80000 524288
NOT_DELEGATED 0x100000 1048576
USE_DES_KEY_ONLY 0x200000 2097152
DONT_REQ_PREAUTH 0x400000 4194304
PASSWORD_EXPIRED 0x800000 8388608
TRUSTED_TO_AUTH_FOR_DELEGATION 0x1000000 16777216
PARTIAL_SECRETS_ACCOUNT 0x04000000 67108864
You must make a binary-AND of property userAccountControl
with 0x002
. In order to get all locked (i.e. disabled) accounts you can use
(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=2))
For operator 1.2.840.113556.1.4.803
see LDAP Matching Rules
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With