|
4 | 4 | # Tamas Jos (@skelsec)
|
5 | 5 | #
|
6 | 6 |
|
| 7 | +import datetime |
7 | 8 | from msldap.ldap_objects.common import MSLDAP_UAC, vn
|
8 | 9 |
|
9 | 10 | MSADMachine_ATTRS = [
|
@@ -63,6 +64,53 @@ def __init__(self):
|
63 | 64 | self.whenCreated = None
|
64 | 65 | self.servicePrincipalName = None
|
65 | 66 | self.allowedtodelegateto = None
|
| 67 | + |
| 68 | + ## calculated properties |
| 69 | + self.when_pw_change = None #datetime |
| 70 | + self.when_pw_expires = None #datetime |
| 71 | + self.must_change_pw = None #datetime |
| 72 | + self.canLogon = None #bool |
| 73 | + |
| 74 | + # https://msdn.microsoft.com/en-us/library/cc245739.aspx |
| 75 | + def calc_PasswordMustChange(self, adinfo): |
| 76 | + # Crtieria 1 |
| 77 | + flags = [MSLDAP_UAC.DONT_EXPIRE_PASSWD, MSLDAP_UAC.SMARTCARD_REQUIRED, MSLDAP_UAC.INTERDOMAIN_TRUST_ACCOUNT, MSLDAP_UAC.WORKSTATION_TRUST_ACCOUNT, MSLDAP_UAC.SERVER_TRUST_ACCOUNT] |
| 78 | + for flag in flags: |
| 79 | + if flag & self.userAccountControl: |
| 80 | + return datetime.datetime.max #never |
| 81 | + |
| 82 | + #criteria 2 |
| 83 | + if self.pwdLastSet == 0: |
| 84 | + return datetime.datetime.min |
| 85 | + |
| 86 | + if adinfo.maxPwdAge == 0: |
| 87 | + return datetime.datetime.max #never |
| 88 | + |
| 89 | + return (self.pwdLastSet - adinfo.maxPwdAge).replace(tzinfo=None) |
| 90 | + |
| 91 | + |
| 92 | + # https://msdn.microsoft.com/en-us/library/cc223991.aspx |
| 93 | + def calc_CanLogon(self): |
| 94 | + flags = [MSLDAP_UAC.ACCOUNTDISABLE, MSLDAP_UAC.LOCKOUT, MSLDAP_UAC.SMARTCARD_REQUIRED, MSLDAP_UAC.INTERDOMAIN_TRUST_ACCOUNT, MSLDAP_UAC.WORKSTATION_TRUST_ACCOUNT, MSLDAP_UAC.SERVER_TRUST_ACCOUNT] |
| 95 | + for flag in flags: |
| 96 | + if flag & self.userAccountControl: |
| 97 | + return False |
| 98 | + |
| 99 | + if (not (MSLDAP_UAC.DONT_EXPIRE_PASSWD & self.userAccountControl)) and (self.accountExpires.replace(tzinfo=None) - datetime.datetime.now()).total_seconds() < 0: |
| 100 | + return False |
| 101 | + |
| 102 | + # |
| 103 | + # TODO: logonHours check! |
| 104 | + # |
| 105 | + |
| 106 | + if self.must_change_pw == datetime.datetime.min: |
| 107 | + #can logon, but must change the password! |
| 108 | + return True |
| 109 | + |
| 110 | + if (self.must_change_pw - datetime.datetime.now()).total_seconds() < 0: |
| 111 | + return False |
| 112 | + |
| 113 | + return True |
66 | 114 |
|
67 | 115 | @staticmethod
|
68 | 116 | def from_ldap(entry, adinfo = None):
|
@@ -106,6 +154,13 @@ def from_ldap(entry, adinfo = None):
|
106 | 154 | temp = entry['attributes'].get('userAccountControl')
|
107 | 155 | if temp:
|
108 | 156 | adi.userAccountControl = MSLDAP_UAC(temp)
|
| 157 | + |
| 158 | + if adinfo: |
| 159 | + adi.when_pw_change = (adi.pwdLastSet - adinfo.minPwdAge).replace(tzinfo=None) |
| 160 | + adi.when_pw_expires = (adi.pwdLastSet - adinfo.maxPwdAge).replace(tzinfo=None) if adinfo.maxPwdAge != 0 else adi.pwdLastSet |
| 161 | + adi.must_change_pw = adi.calc_PasswordMustChange(adinfo) #datetime |
| 162 | + adi.canLogon = adi.calc_CanLogon() #bool |
| 163 | + |
109 | 164 | return adi
|
110 | 165 |
|
111 | 166 | def to_dict(self):
|
|
0 commit comments