2018-03-09 13:57:01 +00:00
from __future__ import print_function
2017-04-06 15:53:25 +12:00
import samba
from samba . auth import system_session
from samba . credentials import Credentials , DONT_USE_KERBEROS , MUST_USE_KERBEROS
from ldb import SCOPE_BASE , LdbError
from ldb import ERR_CONSTRAINT_VIOLATION
from ldb import ERR_INVALID_CREDENTIALS
from ldb import Message , MessageElement , Dn
from ldb import FLAG_MOD_REPLACE
from samba import gensec , dsdb
from samba . samdb import SamDB
import samba . tests
from samba . tests import delete_force
from samba . dcerpc import security , samr
from samba . ndr import ndr_unpack
2018-05-11 11:03:03 +12:00
from samba . tests . password_test import PasswordTestCase
2017-04-06 15:53:25 +12:00
import time
2018-05-11 11:03:03 +12:00
class BasePasswordTestCase ( PasswordTestCase ) :
2017-04-06 15:53:25 +12:00
def _open_samr_user ( self , res ) :
self . assertTrue ( " objectSid " in res [ 0 ] )
( domain_sid , rid ) = ndr_unpack ( security . dom_sid , res [ 0 ] [ " objectSid " ] [ 0 ] ) . split ( )
self . assertEquals ( self . domain_sid , domain_sid )
return self . samr . OpenUser ( self . samr_domain , security . SEC_FLAG_MAXIMUM_ALLOWED , rid )
def _check_attribute ( self , res , name , value ) :
if value is None :
self . assertTrue ( name not in res [ 0 ] ,
msg = " attr[ %s ]= %r on dn[ %s ] " %
( name , res [ 0 ] , res [ 0 ] . dn ) )
return
if isinstance ( value , tuple ) :
( mode , value ) = value
else :
mode = " equal "
if mode == " ignore " :
return
if mode == " absent " :
self . assertFalse ( name in res [ 0 ] ,
msg = " attr[ %s ] not missing on dn[ %s ] " %
( name , res [ 0 ] . dn ) )
return
self . assertTrue ( name in res [ 0 ] ,
msg = " attr[ %s ] missing on dn[ %s ] " %
( name , res [ 0 ] . dn ) )
self . assertTrue ( len ( res [ 0 ] [ name ] ) == 1 ,
msg = " attr[ %s ]= %r on dn[ %s ] " %
( name , res [ 0 ] [ name ] , res [ 0 ] . dn ) )
2018-03-09 13:57:01 +00:00
print ( " %s = ' %s ' " % ( name , res [ 0 ] [ name ] [ 0 ] ) )
2017-04-06 15:53:25 +12:00
if mode == " present " :
return
if mode == " equal " :
v = int ( res [ 0 ] [ name ] [ 0 ] )
value = int ( value )
msg = ( " attr[ %s ]=[ %s ] != [ %s ] on dn[ %s ] \n "
" (diff %d ; actual value is %s than expected) " %
( name , v , value , res [ 0 ] . dn , v - value ,
( ' less ' if v < value else ' greater ' ) ) )
self . assertTrue ( v == value , msg )
return
if mode == " greater " :
v = int ( res [ 0 ] [ name ] [ 0 ] )
self . assertTrue ( v > int ( value ) ,
msg = " attr[ %s ]=[ %s ] <= [ %s ] on dn[ %s ] (diff %d ) " %
( name , v , int ( value ) , res [ 0 ] . dn , v - int ( value ) ) )
return
if mode == " less " :
v = int ( res [ 0 ] [ name ] [ 0 ] )
self . assertTrue ( v < int ( value ) ,
msg = " attr[ %s ]=[ %s ] >= [ %s ] on dn[ %s ] (diff %d ) " %
( name , v , int ( value ) , res [ 0 ] . dn , v - int ( value ) ) )
return
self . assertEqual ( mode , not mode , " Invalid Mode[ %s ] " % mode )
2017-04-06 16:26:26 +12:00
def _check_account_initial ( self , userdn ) :
self . _check_account ( userdn ,
badPwdCount = 0 ,
badPasswordTime = 0 ,
logonCount = 0 ,
lastLogon = 0 ,
lastLogonTimestamp = ( " absent " , None ) ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 )
2017-04-06 15:53:25 +12:00
def _check_account ( self , dn ,
badPwdCount = None ,
badPasswordTime = None ,
logonCount = None ,
lastLogon = None ,
lastLogonTimestamp = None ,
lockoutTime = None ,
userAccountControl = None ,
msDSUserAccountControlComputed = None ,
effective_bad_password_count = None ,
2017-04-21 15:21:58 +12:00
msg = None ,
badPwdCountOnly = False ) :
2018-03-09 13:57:01 +00:00
print ( ' -= ' * 36 )
2017-04-06 15:53:25 +12:00
if msg is not None :
2018-03-09 13:57:01 +00:00
print ( " \033 [01;32m %s \033 [00m \n " % msg )
2017-04-06 15:53:25 +12:00
attrs = [
" objectSid " ,
" badPwdCount " ,
" badPasswordTime " ,
" lastLogon " ,
" lastLogonTimestamp " ,
" logonCount " ,
" lockoutTime " ,
" userAccountControl " ,
" msDS-User-Account-Control-Computed "
]
# in order to prevent some time resolution problems we sleep for
# 10 micro second
time . sleep ( 0.01 )
res = self . ldb . search ( dn , scope = SCOPE_BASE , attrs = attrs )
self . assertTrue ( len ( res ) == 1 )
self . _check_attribute ( res , " badPwdCount " , badPwdCount )
self . _check_attribute ( res , " lockoutTime " , lockoutTime )
2017-04-21 15:21:58 +12:00
self . _check_attribute ( res , " badPasswordTime " , badPasswordTime )
if not badPwdCountOnly :
self . _check_attribute ( res , " logonCount " , logonCount )
self . _check_attribute ( res , " lastLogon " , lastLogon )
self . _check_attribute ( res , " lastLogonTimestamp " , lastLogonTimestamp )
self . _check_attribute ( res , " userAccountControl " , userAccountControl )
self . _check_attribute ( res , " msDS-User-Account-Control-Computed " ,
msDSUserAccountControlComputed )
2017-04-06 15:53:25 +12:00
2017-04-21 15:21:58 +12:00
lastLogon = int ( res [ 0 ] [ " lastLogon " ] [ 0 ] )
logonCount = int ( res [ 0 ] [ " logonCount " ] [ 0 ] )
2017-04-06 15:53:25 +12:00
samr_user = self . _open_samr_user ( res )
uinfo3 = self . samr . QueryUserInfo ( samr_user , 3 )
uinfo5 = self . samr . QueryUserInfo ( samr_user , 5 )
uinfo16 = self . samr . QueryUserInfo ( samr_user , 16 )
uinfo21 = self . samr . QueryUserInfo ( samr_user , 21 )
self . samr . Close ( samr_user )
expected_acb_info = 0
2017-04-21 15:21:58 +12:00
if not badPwdCountOnly :
if userAccountControl & dsdb . UF_NORMAL_ACCOUNT :
expected_acb_info | = samr . ACB_NORMAL
if userAccountControl & dsdb . UF_ACCOUNTDISABLE :
expected_acb_info | = samr . ACB_DISABLED
if userAccountControl & dsdb . UF_PASSWD_NOTREQD :
expected_acb_info | = samr . ACB_PWNOTREQ
if msDSUserAccountControlComputed & dsdb . UF_LOCKOUT :
expected_acb_info | = samr . ACB_AUTOLOCK
if msDSUserAccountControlComputed & dsdb . UF_PASSWORD_EXPIRED :
expected_acb_info | = samr . ACB_PW_EXPIRED
self . assertEquals ( uinfo3 . acct_flags , expected_acb_info )
self . assertEquals ( uinfo3 . last_logon , lastLogon )
self . assertEquals ( uinfo3 . logon_count , logonCount )
2017-04-06 15:53:25 +12:00
expected_bad_password_count = 0
if badPwdCount is not None :
expected_bad_password_count = badPwdCount
if effective_bad_password_count is None :
effective_bad_password_count = expected_bad_password_count
self . assertEquals ( uinfo3 . bad_password_count , expected_bad_password_count )
2017-04-21 15:21:58 +12:00
if not badPwdCountOnly :
self . assertEquals ( uinfo5 . acct_flags , expected_acb_info )
self . assertEquals ( uinfo5 . bad_password_count , effective_bad_password_count )
self . assertEquals ( uinfo5 . last_logon , lastLogon )
self . assertEquals ( uinfo5 . logon_count , logonCount )
self . assertEquals ( uinfo16 . acct_flags , expected_acb_info )
2017-04-06 15:53:25 +12:00
2017-04-21 15:21:58 +12:00
self . assertEquals ( uinfo21 . acct_flags , expected_acb_info )
self . assertEquals ( uinfo21 . bad_password_count , effective_bad_password_count )
self . assertEquals ( uinfo21 . last_logon , lastLogon )
self . assertEquals ( uinfo21 . logon_count , logonCount )
2017-04-06 15:53:25 +12:00
# check LDAP again and make sure the samr.QueryUserInfo
# doesn't have any impact.
res2 = self . ldb . search ( dn , scope = SCOPE_BASE , attrs = attrs )
self . assertEquals ( res [ 0 ] , res2 [ 0 ] )
# in order to prevent some time resolution problems we sleep for
# 10 micro second
time . sleep ( 0.01 )
return res
2018-03-19 12:56:14 +13:00
def update_lockout_settings ( self , threshold , duration , observation_window ) :
""" Updates the global user lockout settings """
m = Message ( )
m . dn = Dn ( self . ldb , self . base_dn )
account_lockout_duration_ticks = - int ( duration * ( 1e7 ) )
m [ " lockoutDuration " ] = MessageElement ( str ( account_lockout_duration_ticks ) ,
FLAG_MOD_REPLACE , " lockoutDuration " )
m [ " lockoutThreshold " ] = MessageElement ( str ( threshold ) ,
FLAG_MOD_REPLACE , " lockoutThreshold " )
lockout_observation_window_ticks = - int ( observation_window * ( 1e7 ) )
m [ " lockOutObservationWindow " ] = MessageElement ( str ( lockout_observation_window_ticks ) ,
FLAG_MOD_REPLACE , " lockOutObservationWindow " )
self . ldb . modify ( m )
2017-04-06 15:53:25 +12:00
def _readd_user ( self , creds , lockOutObservationWindow = 0 ) :
username = creds . get_username ( )
userpass = creds . get_password ( )
userdn = " cn= %s ,cn=users, %s " % ( username , self . base_dn )
2017-04-06 16:21:53 +12:00
delete_force ( self . ldb , userdn )
self . ldb . add ( {
" dn " : userdn ,
" objectclass " : " user " ,
" sAMAccountName " : username } )
self . addCleanup ( delete_force , self . ldb , userdn )
# Sets the initial user password with a "special" password change
# I think that this internally is a password set operation and it can
# only be performed by someone which has password set privileges on the
# account (at least in s4 we do handle it like that).
self . ldb . modify_ldif ( """
dn : """ + userdn + """
changetype : modify
delete : userPassword
add : userPassword
userPassword : """ + userpass + """
""" )
# Enables the user account
self . ldb . enable_account ( " (sAMAccountName= %s ) " % username )
use_kerberos = creds . get_kerberos_state ( )
fail_creds = self . insta_creds ( self . template_creds ,
username = username ,
userpass = userpass + " X " ,
kerberos_state = use_kerberos )
2017-04-06 16:26:26 +12:00
self . _check_account_initial ( userdn )
2017-04-06 16:21:53 +12:00
# Fail once to get a badPasswordTime
try :
ldb = SamDB ( url = self . host_url , credentials = fail_creds , lp = self . lp )
self . fail ( )
2018-02-23 14:34:23 +00:00
except LdbError as e :
( num , msg ) = e . args
2017-04-06 16:21:53 +12:00
self . assertEquals ( num , ERR_INVALID_CREDENTIALS )
# Succeed to reset everything to 0
ldb = SamDB ( url = self . host_url , credentials = creds , lp = self . lp )
return ldb
2017-04-06 15:53:25 +12:00
def assertLoginFailure ( self , url , creds , lp , errno = ERR_INVALID_CREDENTIALS ) :
try :
ldb = SamDB ( url = url , credentials = creds , lp = lp )
self . fail ( " Login unexpectedly succeeded " )
2018-02-23 14:34:23 +00:00
except LdbError as e1 :
( num , msg ) = e1 . args
2017-04-06 15:53:25 +12:00
if errno is not None :
self . assertEquals ( num , errno , ( " Login failed in the wrong way "
" (got err %d , expected %d ) " %
( num , errno ) ) )
def setUp ( self ) :
super ( BasePasswordTestCase , self ) . setUp ( )
self . global_creds . set_gensec_features ( self . global_creds . get_gensec_features ( ) |
gensec . FEATURE_SEAL )
self . template_creds = Credentials ( )
self . template_creds . set_username ( " testuser " )
self . template_creds . set_password ( " thatsAcomplPASS1 " )
self . template_creds . set_domain ( self . global_creds . get_domain ( ) )
self . template_creds . set_realm ( self . global_creds . get_realm ( ) )
self . template_creds . set_workstation ( self . global_creds . get_workstation ( ) )
self . template_creds . set_gensec_features ( self . global_creds . get_gensec_features ( ) )
self . template_creds . set_kerberos_state ( self . global_creds . get_kerberos_state ( ) )
# Gets back the basedn
base_dn = self . ldb . domain_dn ( )
# Gets back the configuration basedn
configuration_dn = self . ldb . get_config_basedn ( ) . get_linearized ( )
res = self . ldb . search ( base_dn ,
scope = SCOPE_BASE , attrs = [ " lockoutDuration " , " lockOutObservationWindow " , " lockoutThreshold " ] )
if " lockoutDuration " in res [ 0 ] :
lockoutDuration = res [ 0 ] [ " lockoutDuration " ] [ 0 ]
else :
lockoutDuration = 0
if " lockoutObservationWindow " in res [ 0 ] :
lockoutObservationWindow = res [ 0 ] [ " lockoutObservationWindow " ] [ 0 ]
else :
lockoutObservationWindow = 0
if " lockoutThreshold " in res [ 0 ] :
lockoutThreshold = res [ 0 ] [ " lockoutThreshold " ] [ 0 ]
else :
lockoutTreshold = 0
self . addCleanup ( self . ldb . modify_ldif , """
dn : """ + base_dn + """
changetype : modify
replace : lockoutDuration
lockoutDuration : """ + str(lockoutDuration) + """
replace : lockoutObservationWindow
lockoutObservationWindow : """ + str(lockoutObservationWindow) + """
replace : lockoutThreshold
lockoutThreshold : """ + str(lockoutThreshold) + """
""" )
2018-03-19 12:56:14 +13:00
self . base_dn = self . ldb . domain_dn ( )
2017-04-06 15:53:25 +12:00
self . account_lockout_duration = 2
self . lockout_observation_window = 2
2018-07-03 12:27:24 +12:00
self . update_lockout_settings ( threshold = 3 ,
duration = self . account_lockout_duration ,
observation_window = self . lockout_observation_window )
2017-04-06 15:53:25 +12:00
2018-05-11 11:03:03 +12:00
# update DC to allow password changes for the duration of this test
self . allow_password_changes ( )
2017-04-06 15:53:25 +12:00
self . domain_sid = security . dom_sid ( self . ldb . get_domain_sid ( ) )
self . samr = samr . samr ( " ncacn_ip_tcp: %s [seal] " % self . host , self . lp , self . global_creds )
self . samr_handle = self . samr . Connect2 ( None , security . SEC_FLAG_MAXIMUM_ALLOWED )
self . samr_domain = self . samr . OpenDomain ( self . samr_handle , security . SEC_FLAG_MAXIMUM_ALLOWED , self . domain_sid )
2018-01-08 19:18:34 +13:00
self . addCleanup ( self . delete_ldb_connections )
2017-04-06 15:53:25 +12:00
# (Re)adds the test user accounts
self . lockout1krb5_creds = self . insta_creds ( self . template_creds ,
username = " lockout1krb5 " ,
userpass = " thatsAcomplPASS0 " ,
kerberos_state = MUST_USE_KERBEROS )
self . lockout1krb5_ldb = self . _readd_user ( self . lockout1krb5_creds )
self . lockout1ntlm_creds = self . insta_creds ( self . template_creds ,
username = " lockout1ntlm " ,
userpass = " thatsAcomplPASS0 " ,
kerberos_state = DONT_USE_KERBEROS )
self . lockout1ntlm_ldb = self . _readd_user ( self . lockout1ntlm_creds )
2018-01-08 19:18:34 +13:00
def delete_ldb_connections ( self ) :
del self . lockout1krb5_ldb
del self . lockout1ntlm_ldb
del self . ldb
2017-04-06 15:53:25 +12:00
def tearDown ( self ) :
super ( BasePasswordTestCase , self ) . tearDown ( )
def _test_login_lockout ( self , creds ) :
username = creds . get_username ( )
userpass = creds . get_password ( )
userdn = " cn= %s ,cn=users, %s " % ( username , self . base_dn )
use_kerberos = creds . get_kerberos_state ( )
# This unlocks by waiting for account_lockout_duration
if use_kerberos == MUST_USE_KERBEROS :
logoncount_relation = ' greater '
lastlogon_relation = ' greater '
2018-03-09 13:57:01 +00:00
print ( " Performs a lockout attempt against LDAP using Kerberos " )
2017-04-06 15:53:25 +12:00
else :
logoncount_relation = ' equal '
lastlogon_relation = ' equal '
2018-03-09 13:57:01 +00:00
print ( " Performs a lockout attempt against LDAP using NTLM " )
2017-04-06 15:53:25 +12:00
# Change password on a connection as another user
res = self . _check_account ( userdn ,
badPwdCount = 0 ,
badPasswordTime = ( " greater " , 0 ) ,
logonCount = ( logoncount_relation , 0 ) ,
lastLogon = ( " greater " , 0 ) ,
lastLogonTimestamp = ( " greater " , 0 ) ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 )
badPasswordTime = int ( res [ 0 ] [ " badPasswordTime " ] [ 0 ] )
logonCount = int ( res [ 0 ] [ " logonCount " ] [ 0 ] )
lastLogon = int ( res [ 0 ] [ " lastLogon " ] [ 0 ] )
firstLogon = lastLogon
lastLogonTimestamp = int ( res [ 0 ] [ " lastLogonTimestamp " ] [ 0 ] )
2018-03-09 13:57:01 +00:00
print ( firstLogon )
print ( lastLogonTimestamp )
2017-04-06 15:53:25 +12:00
self . assertGreater ( lastLogon , badPasswordTime )
self . assertGreaterEqual ( lastLogon , lastLogonTimestamp )
# Open a second LDB connection with the user credentials. Use the
# command line credentials for informations like the domain, the realm
# and the workstation.
creds_lockout = self . insta_creds ( creds )
# The wrong password
creds_lockout . set_password ( " thatsAcomplPASS1x " )
self . assertLoginFailure ( self . host_url , creds_lockout , self . lp )
res = self . _check_account ( userdn ,
badPwdCount = 1 ,
badPasswordTime = ( " greater " , badPasswordTime ) ,
logonCount = logonCount ,
lastLogon = lastLogon ,
lastLogonTimestamp = lastLogonTimestamp ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 ,
msg = ' lastlogontimestamp with wrong password ' )
badPasswordTime = int ( res [ 0 ] [ " badPasswordTime " ] [ 0 ] )
# Correct old password
creds_lockout . set_password ( userpass )
ldb_lockout = SamDB ( url = self . host_url , credentials = creds_lockout , lp = self . lp )
# lastLogonTimestamp should not change
# lastLogon increases if badPwdCount is non-zero (!)
res = self . _check_account ( userdn ,
badPwdCount = 0 ,
badPasswordTime = badPasswordTime ,
logonCount = ( logoncount_relation , logonCount ) ,
lastLogon = ( ' greater ' , lastLogon ) ,
lastLogonTimestamp = lastLogonTimestamp ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 ,
msg = ' LLTimestamp is updated to lastlogon ' )
logonCount = int ( res [ 0 ] [ " logonCount " ] [ 0 ] )
lastLogon = int ( res [ 0 ] [ " lastLogon " ] [ 0 ] )
self . assertGreater ( lastLogon , badPasswordTime )
self . assertGreaterEqual ( lastLogon , lastLogonTimestamp )
# The wrong password
creds_lockout . set_password ( " thatsAcomplPASS1x " )
self . assertLoginFailure ( self . host_url , creds_lockout , self . lp )
res = self . _check_account ( userdn ,
badPwdCount = 1 ,
badPasswordTime = ( " greater " , badPasswordTime ) ,
logonCount = logonCount ,
lastLogon = lastLogon ,
lastLogonTimestamp = lastLogonTimestamp ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 )
badPasswordTime = int ( res [ 0 ] [ " badPasswordTime " ] [ 0 ] )
# The wrong password
creds_lockout . set_password ( " thatsAcomplPASS1x " )
try :
ldb_lockout = SamDB ( url = self . host_url , credentials = creds_lockout , lp = self . lp )
self . fail ( )
2018-02-23 14:34:23 +00:00
except LdbError as e2 :
( num , msg ) = e2 . args
2017-04-06 15:53:25 +12:00
self . assertEquals ( num , ERR_INVALID_CREDENTIALS )
res = self . _check_account ( userdn ,
badPwdCount = 2 ,
badPasswordTime = ( " greater " , badPasswordTime ) ,
logonCount = logonCount ,
lastLogon = lastLogon ,
lastLogonTimestamp = lastLogonTimestamp ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 )
badPasswordTime = int ( res [ 0 ] [ " badPasswordTime " ] [ 0 ] )
2018-03-09 13:57:01 +00:00
print ( " two failed password change " )
2017-04-06 15:53:25 +12:00
# The wrong password
creds_lockout . set_password ( " thatsAcomplPASS1x " )
try :
ldb_lockout = SamDB ( url = self . host_url , credentials = creds_lockout , lp = self . lp )
self . fail ( )
2018-02-23 14:34:23 +00:00
except LdbError as e3 :
( num , msg ) = e3 . args
2017-04-06 15:53:25 +12:00
self . assertEquals ( num , ERR_INVALID_CREDENTIALS )
res = self . _check_account ( userdn ,
badPwdCount = 3 ,
badPasswordTime = ( " greater " , badPasswordTime ) ,
logonCount = logonCount ,
lastLogon = lastLogon ,
lastLogonTimestamp = lastLogonTimestamp ,
lockoutTime = ( " greater " , badPasswordTime ) ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = dsdb . UF_LOCKOUT )
badPasswordTime = int ( res [ 0 ] [ " badPasswordTime " ] [ 0 ] )
lockoutTime = int ( res [ 0 ] [ " lockoutTime " ] [ 0 ] )
# The wrong password
creds_lockout . set_password ( " thatsAcomplPASS1x " )
try :
ldb_lockout = SamDB ( url = self . host_url , credentials = creds_lockout , lp = self . lp )
self . fail ( )
2018-02-23 14:34:23 +00:00
except LdbError as e4 :
( num , msg ) = e4 . args
2017-04-06 15:53:25 +12:00
self . assertEquals ( num , ERR_INVALID_CREDENTIALS )
res = self . _check_account ( userdn ,
badPwdCount = 3 ,
badPasswordTime = badPasswordTime ,
logonCount = logonCount ,
lastLogon = lastLogon ,
lastLogonTimestamp = lastLogonTimestamp ,
lockoutTime = lockoutTime ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = dsdb . UF_LOCKOUT )
# The wrong password
creds_lockout . set_password ( " thatsAcomplPASS1x " )
try :
ldb_lockout = SamDB ( url = self . host_url , credentials = creds_lockout , lp = self . lp )
self . fail ( )
2018-02-23 14:34:23 +00:00
except LdbError as e5 :
( num , msg ) = e5 . args
2017-04-06 15:53:25 +12:00
self . assertEquals ( num , ERR_INVALID_CREDENTIALS )
res = self . _check_account ( userdn ,
badPwdCount = 3 ,
badPasswordTime = badPasswordTime ,
logonCount = logonCount ,
lastLogon = lastLogon ,
lastLogonTimestamp = lastLogonTimestamp ,
lockoutTime = lockoutTime ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = dsdb . UF_LOCKOUT )
# The correct password, but we are locked out
creds_lockout . set_password ( userpass )
try :
ldb_lockout = SamDB ( url = self . host_url , credentials = creds_lockout , lp = self . lp )
self . fail ( )
2018-02-23 14:34:23 +00:00
except LdbError as e6 :
( num , msg ) = e6 . args
2017-04-06 15:53:25 +12:00
self . assertEquals ( num , ERR_INVALID_CREDENTIALS )
res = self . _check_account ( userdn ,
badPwdCount = 3 ,
badPasswordTime = badPasswordTime ,
logonCount = logonCount ,
lastLogon = lastLogon ,
lastLogonTimestamp = lastLogonTimestamp ,
lockoutTime = lockoutTime ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = dsdb . UF_LOCKOUT )
# wait for the lockout to end
time . sleep ( self . account_lockout_duration + 1 )
2018-03-09 13:57:01 +00:00
print ( self . account_lockout_duration + 1 )
2017-04-06 15:53:25 +12:00
res = self . _check_account ( userdn ,
badPwdCount = 3 , effective_bad_password_count = 0 ,
badPasswordTime = badPasswordTime ,
logonCount = logonCount ,
lockoutTime = lockoutTime ,
lastLogon = lastLogon ,
lastLogonTimestamp = lastLogonTimestamp ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 )
# The correct password after letting the timeout expire
creds_lockout . set_password ( userpass )
creds_lockout2 = self . insta_creds ( creds_lockout )
ldb_lockout = SamDB ( url = self . host_url , credentials = creds_lockout2 , lp = self . lp )
time . sleep ( 3 )
res = self . _check_account ( userdn ,
badPwdCount = 0 ,
badPasswordTime = badPasswordTime ,
logonCount = ( logoncount_relation , logonCount ) ,
lastLogon = ( lastlogon_relation , lastLogon ) ,
lastLogonTimestamp = lastLogonTimestamp ,
lockoutTime = 0 ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 ,
msg = " lastLogon is way off " )
logonCount = int ( res [ 0 ] [ " logonCount " ] [ 0 ] )
lastLogon = int ( res [ 0 ] [ " lastLogon " ] [ 0 ] )
# The wrong password
creds_lockout . set_password ( " thatsAcomplPASS1x " )
try :
ldb_lockout = SamDB ( url = self . host_url , credentials = creds_lockout , lp = self . lp )
self . fail ( )
2018-02-23 14:34:23 +00:00
except LdbError as e7 :
( num , msg ) = e7 . args
2017-04-06 15:53:25 +12:00
self . assertEquals ( num , ERR_INVALID_CREDENTIALS )
res = self . _check_account ( userdn ,
badPwdCount = 1 ,
badPasswordTime = ( " greater " , badPasswordTime ) ,
logonCount = logonCount ,
lockoutTime = 0 ,
lastLogon = lastLogon ,
lastLogonTimestamp = lastLogonTimestamp ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 )
badPasswordTime = int ( res [ 0 ] [ " badPasswordTime " ] [ 0 ] )
# The wrong password
creds_lockout . set_password ( " thatsAcomplPASS1x " )
try :
ldb_lockout = SamDB ( url = self . host_url , credentials = creds_lockout , lp = self . lp )
self . fail ( )
2018-02-23 14:34:23 +00:00
except LdbError as e8 :
( num , msg ) = e8 . args
2017-04-06 15:53:25 +12:00
self . assertEquals ( num , ERR_INVALID_CREDENTIALS )
res = self . _check_account ( userdn ,
badPwdCount = 2 ,
badPasswordTime = ( " greater " , badPasswordTime ) ,
logonCount = logonCount ,
lockoutTime = 0 ,
lastLogon = lastLogon ,
lastLogonTimestamp = lastLogonTimestamp ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 )
badPasswordTime = int ( res [ 0 ] [ " badPasswordTime " ] [ 0 ] )
time . sleep ( self . lockout_observation_window + 1 )
res = self . _check_account ( userdn ,
badPwdCount = 2 , effective_bad_password_count = 0 ,
badPasswordTime = badPasswordTime ,
logonCount = logonCount ,
lockoutTime = 0 ,
lastLogon = lastLogon ,
lastLogonTimestamp = lastLogonTimestamp ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 )
# The wrong password
creds_lockout . set_password ( " thatsAcomplPASS1x " )
try :
ldb_lockout = SamDB ( url = self . host_url , credentials = creds_lockout , lp = self . lp )
self . fail ( )
2018-02-23 14:34:23 +00:00
except LdbError as e9 :
( num , msg ) = e9 . args
2017-04-06 15:53:25 +12:00
self . assertEquals ( num , ERR_INVALID_CREDENTIALS )
res = self . _check_account ( userdn ,
badPwdCount = 1 ,
badPasswordTime = ( " greater " , badPasswordTime ) ,
logonCount = logonCount ,
lockoutTime = 0 ,
lastLogon = lastLogon ,
lastLogonTimestamp = lastLogonTimestamp ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 )
badPasswordTime = int ( res [ 0 ] [ " badPasswordTime " ] [ 0 ] )
# The correct password without letting the timeout expire
creds_lockout . set_password ( userpass )
ldb_lockout = SamDB ( url = self . host_url , credentials = creds_lockout , lp = self . lp )
res = self . _check_account ( userdn ,
badPwdCount = 0 ,
badPasswordTime = badPasswordTime ,
logonCount = ( logoncount_relation , logonCount ) ,
lockoutTime = 0 ,
lastLogon = ( " greater " , lastLogon ) ,
lastLogonTimestamp = lastLogonTimestamp ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 )
def _test_multiple_logon ( self , creds ) :
# Test the happy case in which a user logs on correctly, then
# logs on correctly again, so that the bad password and
# lockout times are both zero the second time. The lastlogon
# time should increase.
# Open a second LDB connection with the user credentials. Use the
# command line credentials for informations like the domain, the realm
# and the workstation.
username = creds . get_username ( )
userdn = " cn= %s ,cn=users, %s " % ( username , self . base_dn )
use_kerberos = creds . get_kerberos_state ( )
if use_kerberos == MUST_USE_KERBEROS :
2018-03-09 13:57:01 +00:00
print ( " Testing multiple logon with Kerberos " )
2017-04-06 15:53:25 +12:00
logoncount_relation = ' greater '
lastlogon_relation = ' greater '
else :
2018-03-09 13:57:01 +00:00
print ( " Testing multiple logon with NTLM " )
2017-04-06 15:53:25 +12:00
logoncount_relation = ' equal '
lastlogon_relation = ' equal '
SamDB ( url = self . host_url , credentials = self . insta_creds ( creds ) , lp = self . lp )
res = self . _check_account ( userdn ,
badPwdCount = 0 ,
badPasswordTime = ( " greater " , 0 ) ,
logonCount = ( logoncount_relation , 0 ) ,
lastLogon = ( " greater " , 0 ) ,
lastLogonTimestamp = ( " greater " , 0 ) ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 )
badPasswordTime = int ( res [ 0 ] [ " badPasswordTime " ] [ 0 ] )
logonCount = int ( res [ 0 ] [ " logonCount " ] [ 0 ] )
lastLogon = int ( res [ 0 ] [ " lastLogon " ] [ 0 ] )
lastLogonTimestamp = int ( res [ 0 ] [ " lastLogonTimestamp " ] [ 0 ] )
firstLogon = lastLogon
2018-03-09 13:57:01 +00:00
print ( " last logon is %d " % lastLogon )
2017-04-06 15:53:25 +12:00
self . assertGreater ( lastLogon , badPasswordTime )
self . assertGreaterEqual ( lastLogon , lastLogonTimestamp )
time . sleep ( 1 )
SamDB ( url = self . host_url , credentials = self . insta_creds ( creds ) , lp = self . lp )
res = self . _check_account ( userdn ,
badPwdCount = 0 ,
badPasswordTime = badPasswordTime ,
logonCount = ( logoncount_relation , logonCount ) ,
lastLogon = ( lastlogon_relation , lastLogon ) ,
lastLogonTimestamp = lastLogonTimestamp ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 ,
msg = ( " second logon, firstlogon was %s " %
firstLogon ) )
lastLogon = int ( res [ 0 ] [ " lastLogon " ] [ 0 ] )
time . sleep ( 1 )
SamDB ( url = self . host_url , credentials = self . insta_creds ( creds ) , lp = self . lp )
res = self . _check_account ( userdn ,
badPwdCount = 0 ,
badPasswordTime = badPasswordTime ,
logonCount = ( logoncount_relation , logonCount ) ,
lastLogon = ( lastlogon_relation , lastLogon ) ,
lastLogonTimestamp = lastLogonTimestamp ,
userAccountControl =
dsdb . UF_NORMAL_ACCOUNT ,
msDSUserAccountControlComputed = 0 )