Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect if an Active Directory user account is locked using LDAP in Python

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 userAccountControl LOCKED flag is not used by AD;
  • The lockoutTime attribute should be used instead

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.

like image 496
Daniel Reis Avatar asked Aug 03 '12 11:08

Daniel Reis


People also ask

How can I tell if an Active Directory account is locked?

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.

What UserAccountControl 544?

UserAccountControl value 544 means that the account is enabled but must to change password on next logon.

Who locked account Active Directory?

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.


3 Answers

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.

like image 85
Harvey Kwok Avatar answered Sep 27 '22 22:09

Harvey Kwok


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.

like image 39
rbrayb Avatar answered Sep 27 '22 22:09

rbrayb


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

like image 41
Wernfried Domscheit Avatar answered Sep 27 '22 23:09

Wernfried Domscheit