1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-22 13:34:15 +03:00

tests: Add a sub-set of tests to show the restored DC is sound

+ Add a new ldapcmp_restoredc.sh test that asserts that the original DC
backed up (backupfromdc) matches the new restored DC.
+ Add a new join_ldapcmp.sh test that asserts we can join a given DC,
and that the resulting DB matches the joined DC
+ Add a new login_basics.py test that sanity-checks Kerberos and NTLM
user login works. (This reuses the password_lockout base code, without
taking as long as the password_lockout tests do). Basic LDAP and SAMR
connections are also tested as a side-effect.
+ run the netlogonsvc test against the restored DC to prove we can
establish a netlogon connection.
+ run the same subset of rpc.echo tests that we do for RODC
+ run dbcheck over the new testenvs at the end of the test run

Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
This commit is contained in:
Tim Beale 2018-06-21 15:04:00 +12:00 committed by Andrew Bartlett
parent ccba77a9d8
commit 00d22122e5
4 changed files with 314 additions and 4 deletions

View File

@ -0,0 +1,184 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Basic sanity-checks of user login. This sanity-checks that a user can login
# over both NTLM and Kerberos, that incorrect passwords are rejected, and that
# the user can change their password successfully.
#
# Copyright Andrew Bartlett 2018
#
from __future__ import print_function
import optparse
import sys
from samba.tests.subunitrun import TestProgram, SubunitOptions
import samba.getopt as options
from samba.auth import system_session
from samba.credentials import MUST_USE_KERBEROS
from samba.dsdb import UF_NORMAL_ACCOUNT
from samba.samdb import SamDB
from password_lockout_base import BasePasswordTestCase
sys.path.insert(0, "bin/python")
parser = optparse.OptionParser("password_lockout.py [options] <host>")
sambaopts = options.SambaOptions(parser)
parser.add_option_group(sambaopts)
parser.add_option_group(options.VersionOptions(parser))
# use command line creds if available
credopts = options.CredentialsOptions(parser)
parser.add_option_group(credopts)
subunitopts = SubunitOptions(parser)
parser.add_option_group(subunitopts)
opts, args = parser.parse_args()
if len(args) < 1:
parser.print_usage()
sys.exit(1)
host = args[0]
lp = sambaopts.get_loadparm()
global_creds = credopts.get_credentials(lp)
#
# Tests start here
#
class BasicUserAuthTests(BasePasswordTestCase):
def setUp(self):
self.host = host
self.host_url = host_url
self.lp = lp
self.global_creds = global_creds
self.ldb = SamDB(url=self.host_url, credentials=self.global_creds,
session_info=system_session(self.lp), lp=self.lp)
super(BasicUserAuthTests, self).setUp()
def _test_login_basics(self, creds):
username = creds.get_username()
userpass = creds.get_password()
userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
if creds.get_kerberos_state() == MUST_USE_KERBEROS:
logoncount_relation = 'greater'
lastlogon_relation = 'greater'
print("Performs a lockout attempt against LDAP using Kerberos")
else:
logoncount_relation = 'equal'
lastlogon_relation = 'equal'
print("Performs a lockout attempt against LDAP using NTLM")
# get the intial logon values for this user
res = self._check_account(userdn,
badPwdCount=0,
badPasswordTime=("greater", 0),
logonCount=(logoncount_relation, 0),
lastLogon=("greater", 0),
lastLogonTimestamp=("greater", 0),
userAccountControl=UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0,
msg='Initial test setup...')
badPasswordTime = int(res[0]["badPasswordTime"][0])
logonCount = int(res[0]["logonCount"][0])
lastLogon = int(res[0]["lastLogon"][0])
lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
test_creds = self.insta_creds(creds)
# check logging in with the wrong password fails
test_creds.set_password("thatsAcomplPASS1xBAD")
self.assertLoginFailure(self.host_url, test_creds, self.lp)
res = self._check_account(userdn,
badPwdCount=1,
badPasswordTime=("greater", badPasswordTime),
logonCount=logonCount,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0,
msg='Test login with wrong password')
badPasswordTime = int(res[0]["badPasswordTime"][0])
# check logging in with the correct password succeeds
test_creds.set_password(userpass)
user_ldb = SamDB(url=self.host_url, credentials=test_creds, lp=self.lp)
res = self._check_account(userdn,
badPwdCount=0,
badPasswordTime=badPasswordTime,
logonCount=(logoncount_relation, logonCount),
lastLogon=('greater', lastLogon),
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0,
msg='Test login with correct password')
logonCount = int(res[0]["logonCount"][0])
lastLogon = int(res[0]["lastLogon"][0])
# check that the user can change its password
new_password = "thatsAcomplPASS2"
user_ldb.modify_ldif("""
dn: %s
changetype: modify
delete: userPassword
userPassword: %s
add: userPassword
userPassword: %s
""" % (userdn, userpass, new_password))
# discard the old creds (i.e. get rid of our valid Kerberos ticket)
del test_creds
test_creds = self.insta_creds(creds)
test_creds.set_password(userpass)
# for Kerberos, logging in with the old password fails
if creds.get_kerberos_state() == MUST_USE_KERBEROS:
self.assertLoginFailure(self.host_url, test_creds, self.lp)
info_msg = 'Test Kerberos login with old password fails'
expectBadPwdTime = ("greater", badPasswordTime)
res = self._check_account(userdn,
badPwdCount=1,
badPasswordTime=expectBadPwdTime,
logonCount=logonCount,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0,
msg=info_msg)
badPasswordTime = int(res[0]["badPasswordTime"][0])
else:
# for NTLM, logging in with the old password succeeds
user_ldb = SamDB(url=self.host_url, credentials=test_creds,
lp=self.lp)
info_msg = 'Test NTLM login with old password succeeds'
res = self._check_account(userdn,
badPwdCount=0,
badPasswordTime=badPasswordTime,
logonCount=logonCount,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0,
msg=info_msg)
# check logging in with the new password succeeds
test_creds.set_password(new_password)
user_ldb = SamDB(url=self.host_url, credentials=test_creds, lp=self.lp)
res = self._check_account(userdn,
badPwdCount=0,
badPasswordTime=badPasswordTime,
logonCount=(logoncount_relation, logonCount),
lastLogon=(lastlogon_relation, lastLogon),
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0,
msg='Test login with new password succeeds')
def test_login_basics_krb5(self):
self._test_login_basics(self.lockout1krb5_creds)
def test_login_basics_ntlm(self):
self._test_login_basics(self.lockout1ntlm_creds)
host_url = "ldap://%s" % host
TestProgram(module=__name__, opts=subunitopts)

View File

@ -811,6 +811,17 @@ plantestsuite_loadlist("samba4.ldap.sort.python(ad_dc_ntvfs)", "ad_dc_ntvfs", [p
plantestsuite_loadlist("samba4.ldap.vlv.python(ad_dc_ntvfs)", "ad_dc_ntvfs", [python, os.path.join(samba4srcdir, "dsdb/tests/python/vlv.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) plantestsuite_loadlist("samba4.ldap.vlv.python(ad_dc_ntvfs)", "ad_dc_ntvfs", [python, os.path.join(samba4srcdir, "dsdb/tests/python/vlv.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT'])
plantestsuite_loadlist("samba4.ldap.linked_attributes.python(ad_dc_ntvfs)", "ad_dc_ntvfs:local", [python, os.path.join(samba4srcdir, "dsdb/tests/python/linked_attributes.py"), '$PREFIX_ABS/ad_dc_ntvfs/private/sam.ldb', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) plantestsuite_loadlist("samba4.ldap.linked_attributes.python(ad_dc_ntvfs)", "ad_dc_ntvfs:local", [python, os.path.join(samba4srcdir, "dsdb/tests/python/linked_attributes.py"), '$PREFIX_ABS/ad_dc_ntvfs/private/sam.ldb', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT'])
# These should be the first tests run against testenvs created by backup/restore
for env in ['restoredc']:
# check that a restored DC matches the original DC (backupfromdc)
plantestsuite("samba4.blackbox.ldapcmp_restore", env,
["PYTHON=%s" % python,
os.path.join(bbdir, "ldapcmp_restoredc.sh"),
'$PREFIX_ABS/backupfromdc', '$PREFIX_ABS/%s' % env])
# basic test that we can join the testenv DC
plantestsuite("samba4.blackbox.join_ldapcmp", env,
["PYTHON=%s" % python, os.path.join(bbdir, "join_ldapcmp.sh")])
plantestsuite_loadlist("samba4.ldap.rodc.python(rodc)", "rodc", plantestsuite_loadlist("samba4.ldap.rodc.python(rodc)", "rodc",
[python, [python,
os.path.join(samba4srcdir, "dsdb/tests/python/rodc.py"), os.path.join(samba4srcdir, "dsdb/tests/python/rodc.py"),
@ -857,6 +868,13 @@ for env in ["ad_dc_ntvfs"]:
extra_path=[os.path.join(samba4srcdir, 'dsdb/tests/python')] extra_path=[os.path.join(samba4srcdir, 'dsdb/tests/python')]
) )
# this is a basic sanity-check of Kerberos/NTLM user login
for env in ["restoredc"]:
plantestsuite_loadlist("samba4.ldap.login_basics.python(%s)" % env, env,
[python, os.path.join(samba4srcdir, "dsdb/tests/python/login_basics.py"),
"$SERVER", '-U"$USERNAME%$PASSWORD"', "-W$DOMAIN", "--realm=$REALM",
'$LOADLIST', '$LISTOPT'])
planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.upgradeprovisionneeddc") planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.upgradeprovisionneeddc")
planpythontestsuite("ad_dc:local", "samba.tests.posixacl", py3_compatible=True) planpythontestsuite("ad_dc:local", "samba.tests.posixacl", py3_compatible=True)
planpythontestsuite("ad_dc_no_nss:local", "samba.tests.posixacl", py3_compatible=True) planpythontestsuite("ad_dc_no_nss:local", "samba.tests.posixacl", py3_compatible=True)
@ -887,8 +905,8 @@ t = "rpc.samr.large-dc"
plansmbtorture4testsuite(t, "vampire_dc", ['$SERVER', '-U$USERNAME%$PASSWORD', '--workgroup=$DOMAIN'], modname=("samba4.%s.one" % t)) plansmbtorture4testsuite(t, "vampire_dc", ['$SERVER', '-U$USERNAME%$PASSWORD', '--workgroup=$DOMAIN'], modname=("samba4.%s.one" % t))
plansmbtorture4testsuite(t, "vampire_dc", ['$SERVER', '-U$USERNAME%$PASSWORD', '--workgroup=$DOMAIN'], modname="samba4.%s.two" % t) plansmbtorture4testsuite(t, "vampire_dc", ['$SERVER', '-U$USERNAME%$PASSWORD', '--workgroup=$DOMAIN'], modname="samba4.%s.two" % t)
# some RODC testing # RPC smoke-tests for testenvs of interest (RODC, etc)
for env in ['rodc']: for env in ['rodc', 'restoredc']:
plansmbtorture4testsuite('rpc.echo', env, ['ncacn_np:$SERVER', "-k", "yes", '-U$USERNAME%$PASSWORD', '--workgroup=$DOMAIN'], modname="samba4.rpc.echo") plansmbtorture4testsuite('rpc.echo', env, ['ncacn_np:$SERVER', "-k", "yes", '-U$USERNAME%$PASSWORD', '--workgroup=$DOMAIN'], modname="samba4.rpc.echo")
plansmbtorture4testsuite('rpc.echo', "%s:local" % env, ['ncacn_np:$SERVER', "-k", "yes", '-P', '--workgroup=$DOMAIN'], modname="samba4.rpc.echo") plansmbtorture4testsuite('rpc.echo', "%s:local" % env, ['ncacn_np:$SERVER', "-k", "yes", '-P', '--workgroup=$DOMAIN'], modname="samba4.rpc.echo")
plansmbtorture4testsuite('rpc.echo', "%s:local" % env, ['ncacn_np:$SERVER', "-k", "no", '-Utestallowed\ account%$DC_PASSWORD', '--workgroup=$DOMAIN'], modname="samba4.rpc.echo.testallowed") plansmbtorture4testsuite('rpc.echo', "%s:local" % env, ['ncacn_np:$SERVER', "-k", "no", '-Utestallowed\ account%$DC_PASSWORD', '--workgroup=$DOMAIN'], modname="samba4.rpc.echo.testallowed")
@ -1067,7 +1085,8 @@ for env in [
planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.kcc.kcc_utils") planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.kcc.kcc_utils")
for env in [ "simpleserver", "fileserver", "nt4_dc", "ad_dc", "ad_dc_ntvfs", "ad_member"]: for env in [ "simpleserver", "fileserver", "nt4_dc", "ad_dc", "ad_dc_ntvfs",
"ad_member", "restoredc" ]:
planoldpythontestsuite(env, "netlogonsvc", planoldpythontestsuite(env, "netlogonsvc",
extra_path=[os.path.join(srcdir(), 'python/samba/tests')], extra_path=[os.path.join(srcdir(), 'python/samba/tests')],
name="samba.tests.netlogonsvc.python(%s)" % env) name="samba.tests.netlogonsvc.python(%s)" % env)
@ -1090,7 +1109,8 @@ for env in ['vampire_dc', 'promoted_dc', 'rodc']:
# TODO: Verifying the databases really should be a part of the # TODO: Verifying the databases really should be a part of the
# environment teardown. # environment teardown.
# check the databases are all OK. PLEASE LEAVE THIS AS THE LAST TEST # check the databases are all OK. PLEASE LEAVE THIS AS THE LAST TEST
for env in ["ad_dc_ntvfs", "ad_dc", "fl2000dc", "fl2003dc", "fl2008r2dc", 'vampire_dc', 'promoted_dc']: for env in ["ad_dc_ntvfs", "ad_dc", "fl2000dc", "fl2003dc", "fl2008r2dc",
'vampire_dc', 'promoted_dc', 'backupfromdc', 'restoredc']:
plantestsuite("samba4.blackbox.dbcheck(%s)" % env, env + ":local" , ["PYTHON=%s" % python, os.path.join(bbdir, "dbcheck.sh"), '$PREFIX/provision', configuration]) plantestsuite("samba4.blackbox.dbcheck(%s)" % env, env + ":local" , ["PYTHON=%s" % python, os.path.join(bbdir, "dbcheck.sh"), '$PREFIX/provision', configuration])
# cmocka tests not requiring a specific encironment # cmocka tests not requiring a specific encironment

View File

@ -0,0 +1,41 @@
#!/bin/sh
# Does a join against the testenv's DC and then runs ldapcmp on the resulting DB
. `dirname $0`/subunit.sh
TARGET_DIR="$PREFIX_ABS/join_$SERVER"
cleanup_output_dir()
{
if [ -d $TARGET_DIR ]; then
rm -fr $TARGET_DIR
fi
}
SAMBA_TOOL="$PYTHON $BINDIR/samba-tool"
join_dc() {
JOIN_ARGS="--targetdir=$TARGET_DIR --server=$SERVER -U$USERNAME%$PASSWORD"
$SAMBA_TOOL domain join $REALM dc $JOIN_ARGS --option="netbios name = TESTJOINDC"
}
ldapcmp_result() {
DB1_PATH="tdb://$PREFIX_ABS/$SERVER/private/sam.ldb"
DB2_PATH="tdb://$TARGET_DIR/private/sam.ldb"
# interSiteTopologyGenerator gets periodically updated. With the restored
# testenvs, it can sometimes point to the old/deleted DC object still
$SAMBA_TOOL ldapcmp $DB1_PATH $DB2_PATH --filter=interSiteTopologyGenerator
}
cleanup_output_dir
# check that we can join this DC
testit "check_dc_join" join_dc
# check resulting DB matches server DC
testit "new_db_matches" ldapcmp_result
cleanup_output_dir
exit $failed

View File

@ -0,0 +1,65 @@
#!/bin/sh
# Does an ldapcmp between a newly restored testenv and the original testenv it
# was based on
if [ $# -lt 2 ]; then
cat <<EOF
Usage: $0 ORIG_DC_PREFIX RESTORED_DC_PREFIX
EOF
exit 1;
fi
ORIG_DC_PREFIX_ABS="$1"
RESTORED_DC_PREFIX_ABS="$2"
shift 2
. `dirname $0`/subunit.sh
basedn() {
SAMDB_PATH=$1
$BINDIR/ldbsearch -H $SAMDB_PATH --basedn='' -s base defaultNamingContext | grep defaultNamingContext | awk '{print $2}'
}
ldapcmp_with_orig() {
DB1_PATH="tdb://$ORIG_DC_PREFIX_ABS/private/sam.ldb"
DB2_PATH="tdb://$RESTORED_DC_PREFIX_ABS/private/sam.ldb"
# check if the 2 DCs are in different domains
DC1_BASEDN=$(basedn $DB1_PATH)
DC2_BASEDN=$(basedn $DB2_PATH)
BASE_DN_OPTS=""
# if necessary, pass extra args to ldapcmp to handle the difference in base DNs
if [ "$DC1_BASEDN" != "$DC2_BASEDN" ] ; then
BASE_DN_OPTS="--base=$DC1_BASEDN --base2=$DC2_BASEDN"
fi
# the restored DC will remove DNS entries for the old DC(s)
IGNORE_ATTRS="dnsRecord,dNSTombstoned"
# DC2 joined DC1, so it will have different DRS info
IGNORE_ATTRS="$IGNORE_ATTRS,msDS-NC-Replica-Locations,msDS-HasInstantiatedNCs"
IGNORE_ATTRS="$IGNORE_ATTRS,interSiteTopologyGenerator"
# there's a servicePrincipalName that uses the objectGUID of the DC's NTDS
# Settings that will differ between the two DCs
IGNORE_ATTRS="$IGNORE_ATTRS,servicePrincipalName"
# the restore changes the new DC's password twice
IGNORE_ATTRS="$IGNORE_ATTRS,lastLogonTimestamp"
# The RID pools get bumped during the restore process
IGNORE_ATTRS="$IGNORE_ATTRS,rIDAllocationPool,rIDAvailablePool"
# these are just differences between provisioning a domain and joining a DC
IGNORE_ATTRS="$IGNORE_ATTRS,localPolicyFlags,operatingSystem,displayName"
LDAPCMP_CMD="$PYTHON $BINDIR/samba-tool ldapcmp"
$LDAPCMP_CMD $DB1_PATH $DB2_PATH --two --filter=$IGNORE_ATTRS $BASE_DN_OPTS
}
# check that the restored testenv DC basically matches the original
testit "orig_dc_matches" ldapcmp_with_orig
exit $failed